所有變量(包括基本類型和引用類型)都存在于一個執(zhí)行環(huán)境(也稱作作用域)當中。這個變量決定了變量的生命周期,以及哪一部分代碼可以訪問其中的變量。
變量的執(zhí)行環(huán)境有助于確定應(yīng)該何時釋放內(nèi)存。
JS是一門具有自動垃圾收集機制的編程語言。離開作用域的值將被自動標記為可以回收,在垃圾收集期間被刪除。
當函數(shù)內(nèi)部定義了其它函數(shù)時,就創(chuàng)建了閉包。閉包的作用域鏈包含自己的作用域、包含函數(shù)(外部函數(shù))的作用域和全局作用域。通常函數(shù)作用域及其所有變量都會在函數(shù)執(zhí)行結(jié)束后被銷毀,但是當函數(shù)返回一個閉包時,這個函數(shù)的作用域會一直在內(nèi)存中保存到閉包不存在為止。
function f1() {
var n = 999;
function f2() {
console.log(n);
}
return f2;
}
var result = f1();
result(); // 999
f2可以讀取f1的局部變量,要把f2作為返回值,可以在f1外部讀取它的內(nèi)部變量。
function createIncrementor(start) {
return function () {
return start++;
};
}
var inc = createIncrementor(5);
inc() // 5
inc() // 6
inc() // 7
var add = (function () {
var counter = 0;
return function () {return counter += 1;}
})();
add() //1
add() //2
add() //3
注意: 為什么上面這段代碼沒有直接寫的 function add (){...} 而是把function賦值給了變量add呢?
我們通常會想當然的認為每次調(diào)用 add() 都會重走一遍add()中的代碼塊, 但其實不然。
注意add方法中的return, 它return的并不是1,2,3這樣的數(shù)值,而是return了一個方法,并且把這個方法賦值給了add變量。
那么在這個function自運行一遍之后,其實最后賦值給add的是return counter += 1 這段代碼。
所以后面每次調(diào)用add() 其實都是在調(diào)用return counter += 1。
再結(jié)合之前所說的, 閉包會持有父方法的局部變量并且不會隨父方法銷毀而銷毀, 所以這個counter其實就是來自于第一次function執(zhí)行時創(chuàng)建的變量。
使用閉包可以模仿塊級作用域。
閉包還可以用于在對象中創(chuàng)建私有變量。
function Person(name) {
var _age;
function setAge(n) {
_age = n;
}
function getAge() {
return _age;
}
return {
name: name,
getAge: getAge,
setAge: setAge
};
}
var p1 = Person('張三');
p1.setAge(25);
p1.getAge() // 25
創(chuàng)建閉包必須維護額外的作用域,過度使用它們可能會占用大量內(nèi)存。