閉包


高階函數(shù)除了可以接受函數(shù)作為參數(shù)外,還可以把函數(shù)作為結果值返回坤溃。

function lazy_sum(arr) {
    var sum = function () {
        return arr.reduce(function (x, y) {
            return x + y;
        });
    }
    return sum;
}
  • 當我們調(diào)用lazy_sum()時拍霜,返回的并不是求和結果,而是求和函數(shù):
    var f = lazy_sum([1, 2, 3, 4, 5]); // function sum()
  • 調(diào)用函數(shù)f()時薪介,才真正計算求和的結果:

當lazy_sum返回函數(shù)sum時祠饺,相關參數(shù)和變量都保存在返回的函數(shù)中,這種稱為“閉包(Closure)”的程序結構擁有極大的威力汁政。

請再注意一點道偷,當我們調(diào)用lazy_sum()時,每次調(diào)用都會返回一個新的函數(shù)

var f1 = lazy_sum([1, 2, 3, 4, 5]);
var f2 = lazy_sum([1, 2, 3, 4, 5]);
f1 === f2; // false
f1()和f2()的調(diào)用結果互不影響。

另一個需要注意的問題是挪蹭,返回的函數(shù)并沒有立刻執(zhí)行讽坏,而是直到調(diào)用了f()才執(zhí)行。我們來看一個例子:

function count() {
    var arr = [];
    for (var i=1; i<=3; i++) {
        arr.push(function () {
            return i * i;
        });
    }
    return arr;
}
var results = count();
var f1 = results[0];
var f2 = results[1];
var f3 = results[2];

在上面的例子中换途,每次循環(huán),都創(chuàng)建了一個新的函數(shù)刽射,然后军拟,把創(chuàng)建的3個函數(shù)都添加到一個Array中返回了。
你可能認為調(diào)用f1()誓禁,f2()和f3()結果應該是1懈息,4,9摹恰,但實際結果是:

f1(); // 16
f2(); // 16
f3(); // 16```

原因就在于返回的函數(shù)引用了變量i辫继,但它并非立刻執(zhí)行怒见。等到3個函數(shù)都返回時,它們所引用的變量i已經(jīng)變成了4姑宽,因此最終結果為16遣耍。

`返回函數(shù)不要引用任何循環(huán)變量,或者后續(xù)會發(fā)生變化的變量低千。`

如果一定要引用循環(huán)變量怎么辦配阵?方法是再創(chuàng)建一個函數(shù),用該函數(shù)的參數(shù)綁定循環(huán)變量當前的值示血,無論該循環(huán)變量后續(xù)如何更改棋傍,已綁定到函數(shù)參數(shù)的值不變:

function count() {
var arr = [];
for (var i=1; i<=3; i++) {
arr.push((function (n) {
return function () {
return n * n;
}
})(i));
}
return arr;
}
var results = count();
var f1 = results[0];
var f2 = results[1];
var f3 = results[2];
f1(); // 1
f2(); // 4
f3(); // 9

注意這里用了一個“創(chuàng)建一個匿名函數(shù)并立刻執(zhí)行”的語法:

(function (x) {
return x * x;
})(3); // 9

理論上講,創(chuàng)建一個匿名函數(shù)并立刻執(zhí)行可以這么寫:

function (x) { return x * x } (3);

但是由于JavaScript語法解析的問題难审,會報SyntaxError錯誤瘫拣,因此需要用括號把整個函數(shù)定義括起來:

(function (x) { return x * x }) (3);

通常,一個立即執(zhí)行的匿名函數(shù)可以把函數(shù)體拆開告喊,一般這么寫:

(function (x) {
return x * x;
})(3);

閉包有非常強大的功能麸拄。舉個栗子:

在面向?qū)ο蟮某绦蛟O計語言里,比如Java和C++黔姜,要在對象內(nèi)部封裝一個私有變量拢切,可以用private修飾一個成員變量。
在沒有class機制秆吵,只有函數(shù)的語言里淮椰,借助閉包,同樣可以封裝一個私有變量纳寂。我們用JavaScript創(chuàng)建一個計數(shù)器:

'use strict';
function create_counter(initial) {
var x = initial || 0;
return {
inc: function () {
x += 1;
return x;
}
}
}

它用起來像這樣:

var c1 = create_counter();
c1.inc(); // 1
c1.inc(); // 2
c1.inc(); // 3

var c2 = create_counter(10);
c2.inc(); // 11
c2.inc(); // 12
c2.inc(); // 13

在返回的對象中主穗,實現(xiàn)了一個閉包,該閉包攜帶了局部變量x毙芜,并且忽媒,從外部代碼根本無法訪問到變量x。換句話說腋粥,閉包就是攜帶狀態(tài)的函數(shù)晦雨,并且它的狀態(tài)可以完全對外隱藏起來。

閉包還可以把多參數(shù)的函數(shù)變成單參數(shù)的函數(shù)隘冲。例如金赦,要計算xy可以用Math.pow(x, y)函數(shù),不過考慮到經(jīng)常計算x2或x3对嚼,我們可以利用閉包創(chuàng)建新的函數(shù)pow2和pow3:

function make_pow(n) {
return function (x) {
return Math.pow(x, n);
}
}

// 創(chuàng)建兩個新函數(shù):

var pow2 = make_pow(2);
var pow3 = make_pow(3);

pow2(5); // 25
pow3(7); // 343

最后編輯于
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市绳慎,隨后出現(xiàn)的幾起案子纵竖,更是在濱河造成了極大的恐慌漠烧,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,482評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件靡砌,死亡現(xiàn)場離奇詭異已脓,居然都是意外死亡,警方通過查閱死者的電腦和手機通殃,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,377評論 2 382
  • 文/潘曉璐 我一進店門度液,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人画舌,你說我怎么就攤上這事堕担。” “怎么了曲聂?”我有些...
    開封第一講書人閱讀 152,762評論 0 342
  • 文/不壞的土叔 我叫張陵霹购,是天一觀的道長。 經(jīng)常有香客問我朋腋,道長齐疙,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,273評論 1 279
  • 正文 為了忘掉前任旭咽,我火速辦了婚禮贞奋,結果婚禮上,老公的妹妹穿的比我還像新娘穷绵。我一直安慰自己轿塔,他們只是感情好,可當我...
    茶點故事閱讀 64,289評論 5 373
  • 文/花漫 我一把揭開白布请垛。 她就那樣靜靜地躺著催训,像睡著了一般。 火紅的嫁衣襯著肌膚如雪宗收。 梳的紋絲不亂的頭發(fā)上漫拭,一...
    開封第一講書人閱讀 49,046評論 1 285
  • 那天,我揣著相機與錄音混稽,去河邊找鬼采驻。 笑死,一個胖子當著我的面吹牛匈勋,可吹牛的內(nèi)容都是我干的礼旅。 我是一名探鬼主播,決...
    沈念sama閱讀 38,351評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼洽洁,長吁一口氣:“原來是場噩夢啊……” “哼痘系!你這毒婦竟也來了?” 一聲冷哼從身側響起饿自,我...
    開封第一講書人閱讀 36,988評論 0 259
  • 序言:老撾萬榮一對情侶失蹤汰翠,失蹤者是張志新(化名)和其女友劉穎龄坪,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體复唤,經(jīng)...
    沈念sama閱讀 43,476評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡健田,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,948評論 2 324
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了佛纫。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片妓局。...
    茶點故事閱讀 38,064評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖呈宇,靈堂內(nèi)的尸體忽然破棺而出好爬,到底是詐尸還是另有隱情,我是刑警寧澤攒盈,帶...
    沈念sama閱讀 33,712評論 4 323
  • 正文 年R本政府宣布抵拘,位于F島的核電站,受9級特大地震影響型豁,放射性物質(zhì)發(fā)生泄漏僵蛛。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,261評論 3 307
  • 文/蒙蒙 一迎变、第九天 我趴在偏房一處隱蔽的房頂上張望充尉。 院中可真熱鬧,春花似錦衣形、人聲如沸驼侠。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,264評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽倒源。三九已至,卻和暖如春句狼,著一層夾襖步出監(jiān)牢的瞬間笋熬,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,486評論 1 262
  • 我被黑心中介騙來泰國打工腻菇, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留胳螟,地道東北人。 一個月前我還...
    沈念sama閱讀 45,511評論 2 354
  • 正文 我出身青樓筹吐,卻偏偏與公主長得像糖耸,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子丘薛,可洞房花燭夜當晚...
    茶點故事閱讀 42,802評論 2 345

推薦閱讀更多精彩內(nèi)容