我理解的JavaScript閉包

閉包是前端開(kāi)發(fā)中的一個(gè)重要概念弊仪,也是前端面試的必問(wèn)問(wèn)題之一护赊。對(duì)于JavaScript初學(xué)者而言,閉包學(xué)習(xí)JavaScript的一大障礙。網(wǎng)上有很多閉包的教程已维,形象地告訴了我閉包長(zhǎng)什么樣。但是大部分教程沒(méi)有對(duì)閉包的定義給出精準(zhǔn)的表達(dá)险耀,也沒(méi)有對(duì)閉包背后的一些原理和邏輯進(jìn)行解釋案怯。本文通過(guò)整合網(wǎng)上各路資料,對(duì)閉包前前后后的知識(shí)點(diǎn)進(jìn)行梳理江场,希望可以幫助大家準(zhǔn)確并且深刻理解閉包的概念纺酸。(本文假設(shè)大家對(duì)閉包有一定的理解)

Scope

要理解閉包,先要理解一個(gè)重要概念—作用域址否。

In computer programming, the scope of a name binding – an association of a name to an entity, such as a variable – is the region of a computer program where the binding is valid: where the name can be used to refer to the entity.

Such a region referred to as is a scope block.

參考自wiki百科 Scope (computer science)

scope又可以分為詞法作用域(Lexical scope)和動(dòng)態(tài)作用域(Dynamic scope)餐蔬。兩者區(qū)別與對(duì)區(qū)域這個(gè)概念的解讀。Wiki百科對(duì)兩者的解釋如下:

In languages with lexical scope (also called static scope), name resolution depends on the location in the source code and the lexical context, which is defined by where the named variable or function is defined. In contrast, in languages with dynamic scope the name resolution depends upon the program state when the name is encountered which is determined by the execution context or calling context.

參考自wiki百科 Scope (computer science)

在詞法作用域中佑附,一個(gè)name是否有效取決于它在源代碼中的位置樊诺,也就是詞法上下文。而動(dòng)態(tài)作用域要相對(duì)復(fù)雜一點(diǎn)音同,在動(dòng)態(tài)作用域中词爬,一個(gè)name是否有效取決于這個(gè)程序的運(yùn)行時(shí)狀態(tài),也就是運(yùn)行時(shí)上下文权均。

對(duì)詞法作用域在JavaScript中的表現(xiàn)在本文不作闡述顿膨,具體參考這篇博文:深入理解javascript原型和閉包(12)——簡(jiǎn)介【作用域】

對(duì)Closure的一些定義

各種專業(yè)文獻(xiàn)上的"閉包"(closure)定義非常抽象,很難看懂螺句。我的理解是虽惭,閉包就是能夠讀取其他函數(shù)內(nèi)部變量的函數(shù)。

參考自阮一峰 學(xué)習(xí)Javascript閉包(Closure)

A closure is the combination of a function and the lexical environment within which that function was declared.

參考自MDN Closure

MDN的定義指出了閉包需要的東西:閉包 = 函數(shù) + 函數(shù)定義的詞法上下文環(huán)境蛇尚。阮一峰老師的定義指出了閉包產(chǎn)生的現(xiàn)象:一個(gè)函數(shù)能夠讀取其他函數(shù)內(nèi)部變量芽唇。

In programming languages, closures (also lexical closures or function closures) are techniques for implementing lexically scoped name binding in languages with first-class functions.

參考自wiki百科 Closure(computer programming)

wiki百科上的定義指出了閉包需要的語(yǔ)言條件: first-class functions。關(guān)于這個(gè)知識(shí)點(diǎn)可以參考“函數(shù)是一等公民”背后的含義取劫。另外匆笤,定義中提到的implementing lexically scoped name binding ,即基于詞法作用域的name綁定與scope中的binding概念相互照應(yīng)谱邪。本質(zhì)上就是說(shuō)的就是詞法作用域與變量有效性的關(guān)系炮捧。

在JavaScript中,實(shí)現(xiàn)外部作用域訪問(wèn)內(nèi)部作用域中變量的方法叫做閉包惦银。

參考自《深入淺出Node.js》

以上對(duì)閉包的定義都略有差別咆课,有的將閉包定義為函數(shù)末誓,有的將閉包定義為方法,也有將閉包定義為組合书蚪。我覺(jué)得將閉包理解為一個(gè)方法喇澡,或者某個(gè)東西都對(duì)。兩種定義的方法都對(duì)我們理解閉包有幫助殊校。

JavaScript的閉包

我們都會(huì)遇到在一個(gè)外部函數(shù)套著一個(gè)內(nèi)部函數(shù)的情況晴玖,比如說(shuō):

function foo(x) {
    var tmp = 3;
    function b(y) {
        alert(x + y + (++tmp));
    }
    b(2);
    b(3);
}
foo(0);

在foo函數(shù)結(jié)束的時(shí)候,tmp就會(huì)被銷(xiāo)毀为流。一般來(lái)說(shuō)呕屎,當(dāng)內(nèi)部函數(shù)被return的時(shí)候,外部就可以引用內(nèi)部的函數(shù)敬察,閉包就會(huì)通過(guò)return而產(chǎn)生秀睛。如:

function foo(x) {
    var tmp = 3;
    return function (y) {
        alert(x + y + (++tmp));
    }
}
var bar = foo(2); // bar 現(xiàn)在是一個(gè)閉包
bar(10);

按照我們?cè)镜睦斫猓跊](méi)有閉包的情況下莲祸,foo函數(shù)執(zhí)行完琅催,它內(nèi)部的tmp變量就會(huì)被銷(xiāo)毀,但是因?yàn)橥獠亢瘮?shù)引用了內(nèi)部的變量產(chǎn)生了閉包虫给,內(nèi)部函數(shù)的詞法上下文沒(méi)有被銷(xiāo)毀,tmp變量也沒(méi)有被銷(xiāo)毀侠碧。

當(dāng)然抹估,也有不用閉包的return的例子,比如利用setInterval或者綁定一個(gè)事件等等方法:

function a(){
  var temp = 0;// let也可以
  function b(){
    console.log(temp++);
  }
  // setInterval可以產(chǎn)生閉包
  setInterval(b,1000);
  // 綁定可以產(chǎn)生閉包
  window.addEventListener('click',b);
  // ajax傳入callback可以產(chǎn)生閉包
  ajax(b);
  // 或者直接把這個(gè)函數(shù)傳給window或者其它函數(shù)外部的元素
  window.closure = b;
}
a();

可以看到弄兜,只要內(nèi)部函數(shù)有機(jī)會(huì)在函數(shù)外部被調(diào)用药蜻,或者說(shuō)內(nèi)部函數(shù)被外部的某個(gè)變量引用,就會(huì)產(chǎn)生閉包替饿。就像《深入淺出Node.js》中提到的那樣:

閉包是JavaScript中的高級(jí)特性语泽,利用它可以產(chǎn)生很多巧妙的效果。它的問(wèn)題在于视卢,一旦有變量引用了這個(gè)中間函數(shù)踱卵,這個(gè)中間函數(shù)不會(huì)釋放,同時(shí)也使得原始作用域不會(huì)得到釋放据过。作用域中產(chǎn)生的內(nèi)存占用也不會(huì)被釋放惋砂。除非不再有引用,才會(huì)逐步釋放绳锅。

參考自 《深入淺出Node.js》

參考資料

動(dòng)態(tài)作用域和詞法域的區(qū)別是什么西饵?
“函數(shù)是一等公民”背后的含義
js閉包的概念作用域內(nèi)存模型
阮一峰 學(xué)習(xí)Javascript閉包(Closure)
javascript基礎(chǔ)拾遺——詞法作用域
深入理解javascript原型和閉包(12)——簡(jiǎn)介【作用域】

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市鳞芙,隨后出現(xiàn)的幾起案子眷柔,更是在濱河造成了極大的恐慌期虾,老刑警劉巖,帶你破解...
    沈念sama閱讀 221,548評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件驯嘱,死亡現(xiàn)場(chǎng)離奇詭異镶苞,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)宙拉,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,497評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門(mén)宾尚,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人谢澈,你說(shuō)我怎么就攤上這事煌贴。” “怎么了锥忿?”我有些...
    開(kāi)封第一講書(shū)人閱讀 167,990評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵牛郑,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我敬鬓,道長(zhǎng)淹朋,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 59,618評(píng)論 1 296
  • 正文 為了忘掉前任钉答,我火速辦了婚禮础芍,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘数尿。我一直安慰自己仑性,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,618評(píng)論 6 397
  • 文/花漫 我一把揭開(kāi)白布右蹦。 她就那樣靜靜地躺著诊杆,像睡著了一般。 火紅的嫁衣襯著肌膚如雪何陆。 梳的紋絲不亂的頭發(fā)上晨汹,一...
    開(kāi)封第一講書(shū)人閱讀 52,246評(píng)論 1 308
  • 那天,我揣著相機(jī)與錄音贷盲,去河邊找鬼淘这。 笑死,一個(gè)胖子當(dāng)著我的面吹牛晃洒,可吹牛的內(nèi)容都是我干的慨灭。 我是一名探鬼主播,決...
    沈念sama閱讀 40,819評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼球及,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼氧骤!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起吃引,我...
    開(kāi)封第一講書(shū)人閱讀 39,725評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤筹陵,失蹤者是張志新(化名)和其女友劉穎刽锤,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體朦佩,經(jīng)...
    沈念sama閱讀 46,268評(píng)論 1 320
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡并思,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,356評(píng)論 3 340
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了语稠。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片宋彼。...
    茶點(diǎn)故事閱讀 40,488評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖仙畦,靈堂內(nèi)的尸體忽然破棺而出输涕,到底是詐尸還是另有隱情,我是刑警寧澤慨畸,帶...
    沈念sama閱讀 36,181評(píng)論 5 350
  • 正文 年R本政府宣布莱坎,位于F島的核電站,受9級(jí)特大地震影響寸士,放射性物質(zhì)發(fā)生泄漏檐什。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,862評(píng)論 3 333
  • 文/蒙蒙 一弱卡、第九天 我趴在偏房一處隱蔽的房頂上張望乃正。 院中可真熱鬧,春花似錦婶博、人聲如沸烫葬。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,331評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至垢箕,卻和暖如春划栓,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背条获。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,445評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工忠荞, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人帅掘。 一個(gè)月前我還...
    沈念sama閱讀 48,897評(píng)論 3 376
  • 正文 我出身青樓委煤,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親修档。 傳聞我的和親對(duì)象是個(gè)殘疾皇子碧绞,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,500評(píng)論 2 359

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