js閉包,垃圾回收,內存泄漏

1.閉包的概念

閉包:指有權訪問另外一個函數做用域中的變量的函數。
閉包的本質是將函數內部和函數外部鏈接起來的一座橋樑。javascript

2.如何建立閉包

例1:java

function outer(){
    var a=1;
    function inner(){
        a++;
      alert(a);
    }
    return inner;
}
var f1=outer();//建立了一個閉包,f1能訪問outer函數中的變量
f1();//彈出2.

這段代碼的特色在於:
1.函數inner嵌套在函數outer內部
2.函數outer返回函數inner,並將值賦給了f1
例2:算法

// 實現累加:方式1
var a = 0;
var add = function(){
    a++;
    console.log(a)
}
add();
add();
//方式2 :閉包
var add = (function(){
    var  a = 0;
    return function(){
        a++;
        console.log(a);
    }
})();
console.log(a); //undefined
add();
add();

//方式2的優勢:減小全局變量,將變量私有化

3.閉包與變量的關係

閉包只能取得包含函數中任何變量的最後一個值。
例:chrome

function f1() {
    var res = new Array();
    for(var i=0;i<10;i++){
        res[i] = function() {
            alert(i);
        };
    }
    return res;
}
var f2 = f1();
var f2 = f1();
f2[0]();//alert 10
//並不會返回一次彈出0-9的函數數組,而是彈出10個10的函數數組,由於res中每一個函數的做用域中都保存着f1()的活動對象,引用的是同一個變量i,當f1()返回後i的值爲10

解決方法:數組

function f1() {
    var res = new Array();
    for(var i=0;i<10;i++){
        res[i] = (function(num) {
            return function (){
                alert(num);
            }
        })(i);//函數參數按值傳遞
    }
    return res;
}
var f2 = f1();
var f2 = f1();
f2[0]();//alert 0

4.內存泄露及解決方案

垃圾回收機制閉包

說到內存管理,天然離不開JS中的垃圾回收機制,有兩種策略來實現垃圾回收:標記清除 和 引用計數;函數

標記清除:垃圾收集器在運行的時候會給存儲在內存中的全部變量都加上標記,而後,它會去掉環境中的變量的標記和被環境中的變量引用的變量的標記,此後,若是變量再被標記則表示此變量準備被刪除。 2008年爲止,IE,Firefox,opera,chrome,Safari的javascript都用使用了該方式;設計

引用計數:跟蹤記錄每一個值被引用的次數,當聲明一個變量並將一個引用類型的值賦給該變量時,這個值的引用次數就是1,若是這個值再被賦值給另外一個變量,則引用次數加1。相反,若是一個變量脫離了該值的引用,則該值引用次數減1,當次數爲0時,就會等待垃圾收集器的回收。指針

這個方式存在一個比較大的問題就是循環引用,就是說A對象包含一個指向B的指針,對象B也包含一個指向A的引用。 這就可能形成大量內存得不到回收(內存泄露),由於它們的引用次數永遠不多是 0 。早期的IE版本里(ie4-ie6)採用是計數的垃圾回收機制,閉包致使內存泄露的一個緣由就是這個算法的一個缺陷。code

咱們知道,IE中有一部分對象並非原生額javascript對象,例如,BOM和DOM中的對象就是以COM對象的形式實現的,而COM對象的垃圾回收機制採用的就是引用計數。所以,雖然IE的javascript引擎採用的是標記清除策略,可是訪問COM對象依然是基於引用計數的,所以只要在IE中設計COM對象就會存在循環引用的問題!

例子:

window.onload = function(){
    var ele = document.getElementById("id");
    ele.onclick = function(){
        alert(ele.id);
    }
}

這段代碼爲何會形成內存泄露?

ele.onclick = function(){
        alert(ele.id);
    }

執行這段代碼的時候,將匿名函數對象賦值給ele的onclick屬性;而後匿名函數內部又引用了ele對象,存在循環引用,因此不能被回收。

解決方法:

window.onload = function(){
    var ele = document.getElementById("id");
    var id = ele.id; //解除循環引用
    ele.onclick = function(){
        alert(id); 
    }
    ele = null; // 將閉包引用的外部函數中活動對象清除
}

5.總結閉包的優缺點

優勢:

  • 當須要一個變量常駐內存時,閉包能夠實現一個變量常駐內存 (若是多了就佔用內存了)

  • 避免全局變量的污染

  • 私有化變量

缺點:

  • 由於閉包會攜帶包含它的函數的做用域,所以會比其餘函數佔用更多的內存

  • 引發內存泄露

相關文章
相關標籤/搜索