本文收集了多本書裡對
JavaScript閉包(Closure)
的解釋,或許會對理解閉包有一定幫助。
var fn = f(); // 將函數f 的返回值賦值給變量fn
fn(); // 1
fn(); //2
fn(); //3
function f() {
var cnt = 0;
return function() { return ++cnt; }
}
var fn1 = f1();
fn1(); //1
fn1(); //1
function f1(){
var cnt = 0;
return ++cnt;
}
從表面上來看,閉包是一種具有狀態的函數。或者也可以將閉包的特征理解為,其相關的局部變量在函數調用結束之後將會繼續存在。
通過實現信息隱藏
//使用了閉包的模塊
// 在此調用匿名函數
// 由於匿名函數的返回值是一個函數,所以變量sum 是一個函數
var sum = (function() {
// 無法從函數外部訪問該名稱
// 實際上,這變成了一個私有變量
// 一般來說,在函數被調用之後該名稱就將無法再被訪問
// 不過由於是在被返回的匿名函數中,所以仍可以繼續被使用
var position = { x:2, y:3 };
// 同樣是一個從函數外部無法被訪問的私有變量
// 將其命名為sum 也可以。不過為了避免混淆,這裡采用其他名稱
function sum_internal(a, b) {
return Number(a) + Number(b);
}
// 只不過是為了使用上面的兩個名稱而隨意設計的返回值
return function(a, b) {
print('x = ', position.x);
return sum_internal(a, b); };
}
)();
// 調用
sum(3, 4);
x = 2
7
在利用函數作用域可以封裝名稱,以及閉包可以使名稱在函數調用結束後依然存在這兩個特性後,信息隱藏得以實現。
(function() { 函數體 })();
計數器功能的類
function counter_class(init) { // 初始值可以通過參數設定
var cnt = init || 0; // 設置默認參數的習慣做法(參見5.5 節)
// 如有必要,可在此聲明私有變量與私有函數
return {
// 公有方法
show:function() { print(cnt); },
up:function() { cnt++; return this; }, // return this 在使用方法鏈時很方便
down:function() { cnt--; return this; }
};
}
// 使用代碼
var counter1 = counter_class();
counter1.show();
0
counter1.up();
counter1.show();
1
var counter2 = counter_class(10);
counter2.up().up().up().show(); // 方法鏈
13
表達式閉包
JavaScript 有一種自帶的增強功能,稱為支持函數型程序設計的表達式閉包(Expression closure)。
var sum = function(a, b) { return Number(a) + Number(b); }
//可以省略為
var sum = function(a, b) Number(a) + Number(b);
由於IE9之前的版本對JScript對象和COM對象使用不同的垃圾收集例程,因此閉包在IE的這些版本中會導致一些特殊的問題。具體來說,如果閉包的作用域鏈中保存著一個HTML元素,那麼就意味著該元素將無法被銷毀。
function assignHandler(){
var element = document.getElementById("someElement");
element.onclick = function(){
alert(element.id);
};
}
//把element變量設置為null。這樣就能夠解除對DOM對象的引用,順利地減少其引用數,確保正常回收其占用的內存。
function assignHandler(){
var element = document.getElementById("someElement");
var id = element.id;
element.onclick = function(){
alert(id);
};
element = null;
}
延續局部變量的壽命
//把img變量用閉包封閉起來,便能解決請求丟失的問題
var report = (function(){
var imgs = [];
return function( src ){
var img = new Image();
imgs.push( img );
img.src = src;
}
})();