---------------------這是學習筆記---------------------javascript
隨着前端業務需求的不斷增多,相比之前,咱們會佔用更多的內存。可是內存並非無限的,而對於那些咱們再也不須要的變量、對象該怎麼處理呢?難道一個一個去手動釋放麼?其實並不須要,Javascript 具備自動垃圾回收機制,會按期對那些咱們再也不使用的變量、對象所佔用的內存進行釋放前端
Javascript 會找出再也不使用的變量,再也不使用意味着這個變量生命週期的結束。Javascript 中存在兩種變量——全局變量和局部變量,所有變量的聲明週期會一直持續,直到頁面卸載java
而局部變量聲明在函數中,它的聲明週期從執行函數開始,直到函數執行結束。在這個過程當中,局部變量會在堆或棧上被分配相應的空間以存儲它們的值,函數執行結束,這些局部變量也再也不被使用,它們所佔用的空間也就被釋放數組
可是有一種狀況的局部變量不會隨着函數的結束而被回收,那就是局部變量被函數外部的變量所使用,其中一種狀況就是閉包,由於在函數執行結束後,函數外部的變量依然指向函數內的局部變量,此時的局部變量依然在被使用,因此也就不可以被回收瀏覽器
function func1 () {
const obj = {}
}
function func2 () {
const obj = {}
return obj
}
const a = func1()
const b = func2()
複製代碼
上面這個例子中,func1
執行時爲 obj
分配了一塊內存,可是隨着函數執行結束,obj
佔用的空間也就被釋放了;而 func2
執行時,也爲 obj
分配了內存,可是因爲 obj
最終被返回賦值給了 b
致使其依然被使用,因此 func2
中的 obj
佔用的內存不會被釋放bash
垃圾回收有兩種實現方式,分別是標記清除和引用計數閉包
當變量進入執行環境時標記爲「進入環境」,當變量離開執行環境時則標記爲「離開環境」,被標記爲「進入環境」的變量是不能被回收的,由於它們正在被使用,而標記爲「離開環境」的變量則能夠被回收函數
function func3 () {
const a = 1
const b = 2
// 函數執行時,a b 分別被標記 進入環境
}
func3() // 函數執行結束,a b 被標記 離開環境,被回收
複製代碼
統計引用類型變量聲明後被引用的次數,當次數爲 0 時,該變量將被回收學習
function func4 () {
const c = {} // 引用類型變量 c的引用計數爲 0
let d = c // c 被 d 引用 c的引用計數爲 1
let e = c // c 被 e 引用 c的引用計數爲 2
d = {} // d 再也不引用c c的引用計數減爲 1
e = null // e 再也不引用 c c的引用計數減爲 0 將被回收
}
複製代碼
可是引用計數的方式,有一個相對明顯的缺點——循環引用ui
function func5 () {
let f = {}
let g = {}
f.prop = g
g.prop = f
// 因爲 f 和 g 互相引用,計數永遠不可能爲 0
}
複製代碼
像上面這種狀況就須要手動將變量的內存釋放
f.prop = null
g.prop = null
複製代碼
在現代瀏覽器中,Javascript 使用的方式是標記清楚,因此咱們無需擔憂循環引用的問題
1)全局變量照成內存泄露
function fn() {
name = "你我貸"
}
console.log(name)複製代碼
在 JS 中處理未被聲明的變量, 上述範例中的會把 name , 定義到全局對象中, 在瀏覽器中就是 window 上. 在頁面中的全局變量, 只有當頁面被關閉後纔會被銷燬. 因此這種寫法就會形成內存泄露, 固然在這個例子中泄露的只是一個簡單的字符串, 可是在實際的代碼中, 每每狀況會更加糟糕.
另一種意外建立全局變量的狀況.
function fn() {
this.name = "你我貸"
}
console.log(name)複製代碼
在這種狀況下this被指向了全局變量 window, 意外的建立了全局變量. 咱們談到了一些意外狀況下定義的全局變量, 代碼中也有一些咱們明肯定義的全局變量. 若是使用這些全局變量用來暫存大量的數據, 記得在使用後, 對其從新賦值爲 null.
2)未銷燬的定時器和回調函數照成內存泄露
function fn() {
return 2
}
var oTxt = fn();
setInterval(function() {
var oHtml = document.getElementById("test")
if(oHtml) {
oHtml.innerHTML = oTxt;
}
}, 1000); // 每 1 秒調用一次複製代碼
若是後續 oHtml 元素被移除, 整個定時器實際上沒有任何做用. 但若是你沒有回收定時器, 整個定時器依然有效, 不但定時器沒法被內存回收, 定時器函數中的依賴也沒法回收. 在這個案例中的 fn 也沒法被回收.
3 ) 閉包照成內存泄露
在 JS 開發中, 咱們會常常用到閉包, 一個內部函數, 有權訪問包含其的外部函數中的變量. 下面這種狀況下, 閉包也會形成內存泄露.
3)DOM 引用照成內存泄露
不少時候, 咱們對 Dom 的操做, 會把 Dom 的引用保存在一個數組或者 Map 中.
var elements = {
txt: document.getElementById("test")
}
function fn() {
elements.txt.innerHTML = "1111"
}
function removeTxt() {
document.body.removeChild(document.getElementById('test'));
}
fn();
removeTxt()
console.log(elements.txt)複製代碼
上述案例中, 即便咱們對於 test 元素進行了移除, 可是仍然有對 test 元素的引用, 依然沒法對齊進行內存回收. 另外須要注意的一個點是, 對於一個 Dom 樹的葉子節點的引用. 舉個例子: 若是咱們引用了一個表格中的 td 元素, 一旦在 Dom 中刪除了整個表格, 咱們直觀的以爲內存回收應該回收除了被引用的 td 外的其餘元素. 可是事實上, 這個 td 元素是整個表格的一個子元素, 並保留對於其父元素的引用. 這就會致使對於整個表格, 都沒法進行內存回收. 因此咱們要當心處理對於 Dom 元素的引用.