正確理解Javascript閉包
閉包是ECMAScript一個很重要的特徵,但是卻很難用合適的定義來描述它。雖然閉包很難清晰地描述,但是,卻很容易建立,或者說,不小心建立。然而,閉包的存在其實是有一定的潛在問題的。為了避免“不小心”地建立閉包,以及更好地利用閉包的優點,有必要理解閉包的`機制。
閉包的定義
關於閉包,有太多的定義,特別是有一些定義非常抽象,象這個:
A "closure" is an expression (typically a function) that can have free variables together with an environment that binds those variables.
大致是說閉包是一個表示式,擁有一些自由變數及繫結這些變數的執行環境。這種定義太書面化,反而難以理解。
還有另一個定義:
所有函式都是閉包。這個定義給我很大的迷惑,換句話說,由於Javascript沒有塊級作用域,因此閉包一般指的是函式(想不出除了函式以外還有哪些方式可以構成閉包)。
這裡不想太多討論函式與閉包的關係,下面給出我認為比較容易理解的定義吧。
首先,閉包的存在是基於作用域鏈。由於作用域鏈的機制,所有函式(即使全域性函式)都能引用上下文執行環境中的變數(即free variables)。
其次,閉包內部必須有free variables。順便說下兩種變數1. Local variables (bound variables) 2. Non-local variables (free variables)
最後,在其上下文環境結束後仍然存在。即內部函式擁有比它的外部函式更長的生命週期。
關於閉包定義的解析
關於閉包定義的兩點,一直在考慮是不是必須同時滿足。
首先,如果閉包內部沒有free variables,即是說它沒有訪問外部的變數,那麼就失去了閉包的意義。(除非通過其他閉包改變了行為)因此,我認為free variables是必要條件。
其次,如果函式內部存在free variables,但是當其上下文環境銷燬後,它也跟著銷燬。可以想象內部函式,雖然訪問了其外部函式變數,但是當外部函式執行完後也隨之回收。這種情況下,閉包的討論也沒有意義。
來看兩個例子:
複製程式碼 程式碼如下:
var objectA = (function() {
var localA = "localA";
innerFn();
// 單純的內部函式呼叫
function innerFn() {
localA = "innerChange";
}
return {
getLocalA : function() {
return "empty";
}
};
})();
ocalA();
ocalA = function() {
return localA;
};
//(ocalA()); //error: localA is not defined
var objectB = (function() {
var localB = "localB";
return {
getLocalB : function() {
return "empty";
},
updateGetLocalB : function() {
ocalB = function() {
return localB;
};
},
updateLocalB : function() {
localB = "changeLocalB";
}
};
})();
(ocalB()); // empty
// 通過其他閉包改變
teGetLocalB();
(ocalB()); // localB
teLocalB();
(ocalB()); // changeLocalB
閉包的優點和缺點
閉包的優點是閉包內部可以訪問到定義它們的外部函式的引數和變數(除了this和arguments)。
閉包主要的問題便是它會儲存包含它的函式的作用域,因此比一般函式佔用更多的記憶體空間,因此不宜過度使用閉包。
閉包的應用
閉包最基本的應用場景便是通過保護內部變數從而實現私有,比如模組模式。