JavaScript中的閉包(closure)

閉包的特性javascript

1.函數嵌套函數
2.函數內部能夠引用外部的參數和變量
3.參數和變量不會被垃圾回收機制回收

 閉包的缺點就是常駐內存,會增大內存使用量,使用不當很容易形成內存泄露,主要用於私有的方法和變量php

Javascript的垃圾回收原理

基本上全部語言自動內存管理,用的都是引用計數了java

一、在javascript中,若是一個對象再也不被引用,那麼這個對象就會被GC回收;
二、若是兩個對象互相引用,而再也不被第3者所引用,那麼這兩個互相引用的對象也會被回收。

 閉包的好處json

1.但願一個變量長期駐紮在內存中
2.避免全局變量的污染
3.私有成員的存在

 一、變量的累加,常駐內存segmentfault

示例一閉包

var a = 1;
function abc(){
        a++;
        alert(a);
}
abc();              //2
abc();            //3

示例二模塊化

function abc(){
        var a = 1;
        a++;
        alert(a);
}
abc();                       //2
abc();                    //2

 那麼怎麼才能作到變量a既是局部變量又能夠累加呢?函數

function outer(){
        var x=10;
        return function(){             //函數嵌套函數
                x++;
                alert(x);
        }
}
var y = outer();              //外部函數賦給變量y;
y();                 //y函數調用一次,結果爲11,至關於outer()();
y();                //y函數調用第二次,結果爲12,實現了累加

 二、模塊化代碼,減小全局變量的污染this

var abc = (function(){      //abc爲外部匿名函數的返回值
        var a = 1;
        return function(){
                a++;
                alert(a);
        }
})();
abc();    //2 ;調用一次abc函數,實際上是調用裏面內部函數的返回值    
abc();    //3

 三、私有成員的存在spa

var aaa = (function(){
        var a = 1;
        function bbb(){
                a++;
                alert(a);
        }
        function ccc(){
                a++;
                alert(a);
        }
        return {
                b:bbb,             //json結構
                c:ccc
        }
})();
aaa.b();     //2
aaa.c()      //3

 四、內存泄露問題

因爲IEjs對象和DOM對象使用不一樣的垃圾收集方法,所以閉包在IE中會致使內存泄露問題,也就是沒法銷燬駐留在內存中的元素

function closure(){
    var oDiv = document.getElementById('oDiv');//oDiv用完以後一直駐留在內存中
    oDiv.onclick = function () {
        alert('oDiv.innerHTML');//這裏用oDiv致使內存泄露
    };
}
closure();
//最後應將oDiv解除引用來避免內存泄露
function closure(){
    var oDiv = document.getElementById('oDiv');
    var test = oDiv.innerHTML;
    oDiv.onclick = function () {
        alert(test);
    };
    oDiv = null;
}

 五、閉包引發的誤解 http://segmentfault.com/q/1010000003490094 原文地址

錯誤的寫法

for(var i = 0; i < 10; i++) {
    setTimeout(function() {
        console.log(i);  
    }, 1000);
} // 9 10次

 正確的寫法1

for(var i = 0; i < 10; i++) {
    (function(e) {
        setTimeout(function() {
            console.log(e);  
        }, 1000);
    })(i);
}

 正確的寫法2

for(var i = 0; i < 10; i++) {
    setTimeout((function(e) {
        return function() {
            console.log(e);
        }
    })(i), 1000)
}

 正確的寫法3

for(var i = 0; i < 10; i++) {
    setTimeout(function(e) {
        console.log(e);  
    }, 1000, i);
}

 正確的寫法4 Some legacy JS environments (Internet Explorer 9 & below) do not support this.

for(var i = 0; i < 10; i++) {
    setTimeout(console.log.bind(console, i), 1000);
}

正確的寫法5 固然在ES3/5下除了經過IIFE構造做用域外,還能夠經過with來構造

for(var i = 0; i < 10; ++i) with({i:i}){
   setTimeout(function(){console.log(i)}, 1000)
}

正確的寫法6 使用let表示變量做用域

for(let i = 0; i < 10; ++i){
  setTimeout(function(){console.log(i)}, 1000)
}

解決這個問題的關鍵:弄清楚每種寫法的做用域鏈

最開始的寫法 for =》做用域A,setTimeout=》做用域B,閉包的存在是得外部代碼執行完畢,其任然駐留在內存中,代碼塊B在執行時,找不到變量i,因而沿着做用域鏈向上找,取到A做用域中i的值,此時內存中i值爲10

for(var i = 0; i < 10; i++) { // 做用域A,存儲i的值

    setTimeout(function() {//做用域B
        console.log(i);  
    }, 1000);
}

 而正確的寫法

for(var i = 0; i < 10; i++) { //做用域A,存儲i

(function(e) {   //做用域B0,存儲e0 做用域B1,存儲e1,每循環一次,都有一個單獨的做用域
    setTimeout(function() {//做用域C0,C1,C2,... 對應外部做用域B0,B1...
        console.log(e);  
      }, 1000);
    })(i);
}

 

參考地址

http://segmentfault.com/q/1010000003490094

相關文章
相關標籤/搜索