在討論ECMAScript閉包之前,先來介紹下函數(shù)式編程(與ECMA-262-3 標(biāo)準(zhǔn)無關(guān))中一些基本定義。 然而,為了更好的解釋這些定義,這里還是拿ECMAScript來舉例。
眾所周知,在函數(shù)式語言中(ECMAScript也支持這種風(fēng)格),函數(shù)即是數(shù)據(jù)。就比方說,函數(shù)可以保存在變量中,可以當(dāng)參數(shù)傳遞給其他函數(shù),還可以當(dāng)返回值返回等等。 這類函數(shù)有特殊的名字和結(jié)構(gòu)。
函數(shù)式參數(shù)(“Funarg”) ―― 是指值為函數(shù)的參數(shù)。
如下例子:
function exampleFunc(funArg) { funArg(); } exampleFunc(function () { alert('funArg'); });
上述例子中funArg的實參是一個傳遞給exampleFunc的匿名函數(shù)。
反過來,接受函數(shù)式參數(shù)的函數(shù)稱為 高階函數(shù)(high-order function 簡稱:HOF)。還可以稱作:函數(shù)式函數(shù) 或者 偏數(shù)理的叫法:操作符函數(shù)。 上述例子中,exampleFunc 就是這樣的函數(shù)。
此前提到的,函數(shù)不僅可以作為參數(shù),還可以作為返回值。這類以函數(shù)為返回值的函數(shù)稱為 _帶函數(shù)值的函數(shù)(functions with functional value or function valued functions)。
(function functionValued() {
return function () {
alert('returned function is called');
};
})()();//這種()直接執(zhí)行的方式要熟悉。
可以以正常數(shù)據(jù)形式存在的函數(shù)(比方說:當(dāng)參數(shù)傳遞,接受函數(shù)式參數(shù)或者以函數(shù)值返回)都稱作 第一類函數(shù)(一般說第一類對象)。 在ECMAScript中,所有的函數(shù)都是第一類對象。
接受自己作為參數(shù)的函數(shù),稱為 自應(yīng)用函數(shù)(auto-applicative function 或者 self-applicative function):
(function selfApplicative(funArg) { if (funArg && funArg === selfApplicative) { alert('self-applicative'); return; } selfApplicative(selfApplicative); })();
以自己為返回值的函數(shù)稱為 自復(fù)制函數(shù)(auto-replicative function 或者 self-replicative function)。 通常,“自復(fù)制”這個詞用在文學(xué)作品中:
(function selfReplicative() {
return selfReplicative;
})();
在函數(shù)式參數(shù)中定義的變量,在“funArg”激活時就能夠訪問了(因為存儲上下文數(shù)據(jù)的變量對象每次在進(jìn)入上下文的時候就創(chuàng)建出來了):
function testFn(funArg) { // 激活funArg, 本地變量localVar可訪問 funArg(10); // 20 funArg(20); // 30 } testFn(function (arg) { var localVar = 10; alert(arg + localVar); });
然而,我們知道,在ECMAScript中,函數(shù)是可以封裝在父函數(shù)中的,并可以使用父函數(shù)上下文的變量。 這個特性會引發(fā) funArg問題。
在面向堆棧的編程語言中,函數(shù)的本地變量都是保存在堆棧上的, 每當(dāng)函數(shù)激活的時候,這些變量和函數(shù)參數(shù)都會壓棧到該堆棧上。
當(dāng)函數(shù)返回的時候,這些參數(shù)又會從堆棧中移除。這種模型對將函數(shù)作為函數(shù)式值使用的時候有很大的限制(比方說,作為返回值從父函數(shù)中返回)。 絕大部分情況下,問題會出現(xiàn)在當(dāng)函數(shù)有 自由變量的時候。
自由變量是指在函數(shù)中使用的,但既不是函數(shù)參數(shù)也不是函數(shù)的局部變量的變量
如下所示:
function testFn() { var localVar = 10; function innerFn(innerParam) { alert(innerParam + localVar); } return innerFn; } var someFn = testFn(); someFn(20); // 30
上述例子中,對于innerFn函數(shù)來說,localVar就屬于自由變量。
對于采用 面向堆棧模型來存儲局部變量的系統(tǒng)而言,就意味著當(dāng)testFn函數(shù)調(diào)用結(jié)束后,其局部變量都會從堆棧中移除。 這樣一來,當(dāng)從外部對innerFn進(jìn)行函數(shù)調(diào)用的時候,就會發(fā)生錯誤(因為localVar變量已經(jīng)不存在了)。
而且,上述例子在 面向堆棧實現(xiàn)模型中,要想將innerFn以返回值返回根本是不可能的。 因為它也是testFn函數(shù)的局部變量,也會隨著testFn的返回而移除。
還有一個函數(shù)對象問題和當(dāng)系統(tǒng)采用動態(tài)作用域,函數(shù)作為函數(shù)參數(shù)使用的時候有關(guān)。
看如下例子: