常見的 JavaScript 內存泄露
內存泄漏:由於疏忽或錯誤造成程序未能釋放已經不再使用的內存。內存泄漏並非指內存在物理上的消失,而是應用程序分配某段內存後,由於設計錯誤,導致在釋放該段內存之前就失去了對該段內存的控制,從而造成了內存的浪費。
1、意外的全局變量
js對未聲明變量會在全局最高對象上創建它的引用,(是以屬性存在的,而不是變量),如果在遊覽器上就是window對象,如果在node環境下就是global;如果未聲明的變量緩存大量的數據,它可能只有在頁面被刷新或者被關閉的時候才會釋放內存,這樣就造成了內存意外泄漏。如下例子:
function my() { name="my name is tokey" } my() console.log(window)
在控制臺可以看到
還有通過this創建意外的全局變量
function my() { this.name="my name is tokey" } my() console.log(window.name) //my name is tokey 此時的this指向的並不是undefined而是全局對象window
針對上面類型的內存泄漏我們在平時一定要聲明變量,不要有全局直接引用。(在JavaScript文件中添加 ‘use strict‘
,開啟嚴格模式,可以有效地避免上述問題。)
2、console.log
作為前端平時使用console.log在控制臺打出相對應的信息可以說是非常常見。但如果沒有去掉console.log可能會存在內存泄漏。因為在代碼運行之後需要在開發工具能查看對象信息,所以傳遞給console.log的對象是不能被垃圾回收。
3、閉包
首先閉包是一個函數A返回一個內聯的函數B,及時A函數執行完函數B也可以訪問函數A裏面的變量,這就是一個簡單的閉包。本質上閉包是將函數內部和外部連接起來的一座橋梁。
function my(name) { function sendName() { console.log(name) } return sendName } var test=my("tokey") test() //tokey
在my()內部創建的sendName()函數是不會被回收的,因為它被全局變量test引用,處於隨時被調用的狀態。如果向釋放內存可以設置test=null;
4、DOM泄漏
遊覽器中DOM和js采用的是不一樣的引擎,DOM采用的是渲染引擎,而js采用的是v8引擎,所以在用js操作DOM時會比較耗費性能,因為他們需要橋來鏈接他們。為了減少DOM的操作,我們一般將常用的DOM。我們會采用變量引用的方式會將其緩存在當前環境。如果在進行一些刪除、更新操作之後,可能會忘記釋放已經緩存的DOM,話不多說直接來個例子
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="maximum-scale=1.0,minimum-scale=1.0,user-scalable=0,initial-scale=1.0, width=device-width"> <title>Title</title> <style> *{ margin: 0; padding: 0; } </style> </head> <body> <div class="main"> <div class="test">天</div> <div class="item">天</div> <div class="item">向</div> </div> <button id="add">點擊我增加</button> <button id="remove">點擊我減少</button> </body> <script src="js/Zepto.js"></script> <script src="http://apps.bdimg.com/libs/jquery/2.1.4/jquery.min.js"></script> <script> var add=document.querySelector("#add"); var remove=document.querySelector("#remove"); var main=document.querySelector(".main") var test=document.querySelector(".test") add.onclick=function () { var itemN=document.createElement(‘div‘) var txt=document.createTextNode(‘上‘) itemN.appendChild(txt) main.appendChild(itemN) } remove.onclick=function () { main.removeChild(test) } </script> </html>
通過chrome遊覽器的內存調試工作我們可以看到
在我點擊了三次增加之後,可以明顯的看到節點(綠線)有三次明顯的增加,之後我又刪除了一個節點,但綠線沒有下降,這是為什麽呢?,這也就是內存泄漏。原因就是刪除的DOM在js中有全局的引用。也就是我刪除的test在文中被引用,所以無法釋放內存。所以在刪除更新等操作後應該將其設置為null。
5、被遺忘的timers
js中常用的定時器setInterval()、setTimeout().他們都是規定延遲一定的時間執行某個代碼,而其中setInterval()和鏈式setTimeout()在使用完之後如果沒有手動關閉,會一直存在執行占用內存,所以在不用的時候我們可以通過clearInterval()、clearTimeout() 來關閉其對應的定時器,釋放內存。熟悉朋友都知道這類定時器是有誤差的,所以遊覽器給出了專門的API-requestAnimationFrame();大家可以試一下。
常見的 JavaScript 內存泄露