摘要:以上描述,全部符合閉包的描述,那這就是閉包。二執(zhí)行過程之前的文章講了函數(shù)的執(zhí)行上下文棧,變量對象,作用域鏈等內(nèi)容,接下來通過閉包代碼回顧代碼是怎么樣的執(zhí)行過程。將活動(dòng)對象壓入作用域鏈頂端。函數(shù)執(zhí)行結(jié)束,彈出執(zhí)行上下文棧。
本文一共 1300 字,讀完只需 5 分鐘概述
閉包, 可以說是每個(gè)前端工程師都聽說的一個(gè)詞,咋一看很難從字面上去理解,從而給人留下了閉包是一個(gè)重要又難以理解的概念。
但是,閉包在 JS 代碼可以說是隨處可見,閉包也只是計(jì)算機(jī)領(lǐng)域的一個(gè)概念而已,它的存在是因?yàn)?JS 的一些語言特性,比如:函數(shù)式語言,執(zhí)行上下文,執(zhí)行上下文棧,作用域鏈,詞法作用域。
執(zhí)行上下文:Execution Context
執(zhí)行上下文棧:Execution Context Stack
作用域鏈:Scope Chain
作用域:Scope
本篇文章,將先給結(jié)論,到底什么是閉包,再來分析產(chǎn)生閉包的過程和原因。
一、什么是閉包當(dāng)函數(shù)記住并訪問所在詞法作用域的自由變量時(shí),就產(chǎn)生了閉包,即使函數(shù)是在當(dāng)前詞法作用域外執(zhí)行。
--《你不知道的 JavaScript》
來段經(jīng)典的閉包代碼:
function outter() { var a = 123; function inner() { console.log(a); } return inner; } var foo = outter(); foo(); // 123
內(nèi)部函數(shù) inner 記住了它被定義時(shí)的詞法作用域,也就是 outter 的函數(shù)作用域,并訪問了該作用域里的自由變量 a, 同時(shí),inner 函數(shù)作用返回值,在外部作用域中被執(zhí)行。
以上描述,全部符合閉包的描述,那這就是閉包。
二、執(zhí)行過程之前的文章講了函數(shù)的執(zhí)行上下文棧,變量對象,作用域鏈等內(nèi)容,接下來通過閉包代碼回顧代碼是怎么樣的執(zhí)行過程。
function outter() { var a = 123; function inner() { console.log(a); } return inner; } var foo = outter(); foo(); // 123
進(jìn)入全局代碼的執(zhí)行上下文,全局上下文被壓入執(zhí)行上下文棧。
ECStack = [ globalContext ];
全局上下文創(chuàng)建全局變量對象,創(chuàng)建 this 并指向全局上下文。
globalContext = { VO: global, scope: [global.VO], this: global }
全局上下文初始化時(shí),outter 函數(shù)被創(chuàng)建,建立作用域鏈,復(fù)制 Scope 屬性到 outter 函數(shù)的內(nèi)部屬性[[scope]]
outter.[[scope]] = [ globalContext.VO ];
執(zhí)行 outter 函數(shù),創(chuàng)建 outter 函數(shù)執(zhí)行上下文,將 outter 上下文壓入執(zhí)行上下文棧。
ECStack = [ globalContext, outterContext ];
初始化 outter 函數(shù)執(zhí)行上下文,用 arguments 創(chuàng)建活動(dòng)對象,加入形參、函數(shù)聲明、變量聲明。將活動(dòng)對象壓入 outter 作用域鏈頂端。
outterContext = { AO: { arguments: { a: undefined, } length: 1 }, scope: undefined, inner: reference to function inner(){} Scope: [AO, globalContext.VO], this: undefined }
outter 執(zhí)行完畢,接著執(zhí)行 outter 返回的被變量引用的函數(shù) inner;
ECStack = [ globalContext, innerContext ];
inner 函數(shù)初始化,過程和第4步一樣。
innerContext = { AO: { arguments: { length: 0 } }, Scope: [AO, outterContext.AO, globalContext.VO], this: undefined }
inner 執(zhí)行,沿著作用域鏈查找變量 a, 打印 a 值。
inner 函數(shù)執(zhí)行結(jié)束,彈出執(zhí)行上下文棧。
ECStack = [ globalContext ];
在這個(gè)過程中,第 5 步,outter 已經(jīng)執(zhí)行結(jié)束,執(zhí)行上下文按理來說已經(jīng)被銷毀,內(nèi)部函數(shù) inner 怎么還能訪問 outter 作用域的變量呢。
正是由于閉包,inner 引用了它所在詞法作用域的自由變量 a,inner 的作用域鏈中仍然是完整的, 盡管 inner 在其他地方執(zhí)行,還是返回了正確結(jié)果。
三、函數(shù)式語言閉包中,一個(gè)很重要的特點(diǎn)就是,內(nèi)部函數(shù)作為一個(gè)數(shù)據(jù)被返回。這是由于 JS 是函數(shù)式語言,函數(shù)可以作為參數(shù)傳遞進(jìn)函數(shù),也可以作為一個(gè)數(shù)據(jù)返回。函數(shù)的嵌套構(gòu)成了作用域的嵌套,也就有了作用域鏈。
由于函數(shù)具有作用域,且變量的尋找具有 “遮蔽效應(yīng)”(從內(nèi)到外,找到第一個(gè)就停止),使得局部作用域的變量對于外部作用域是不可見的,于是函數(shù)就有了封閉性,所以我們拿函數(shù)來包裹封裝私有變量,同時(shí)也有了閉包。
四、自由變量自由變量是指在函數(shù)中使用的,但既不是函數(shù)參數(shù)也不是函數(shù)的局部變量的變量。
function outter() { var a = 123; function inner() { console.log(a); } return inner; } var foo = outter(); foo(); // 123
對于 inner 函數(shù)而言,變量 a, 不是它的函數(shù)參數(shù),也不是它的局部變量,a 就是自由變量。
五、閉包的用處和缺點(diǎn)從閉包的特點(diǎn)可以看出,自由變量保存在了內(nèi)存中,并能間接訪問。
那么閉包的作用就是:
隱藏私有變量,解決變量命名空間污染的問題。
缺點(diǎn)
如果閉包過多,變量常駐內(nèi)存,肯定會(huì)占用大量內(nèi)存空間。
由于 JS 是函數(shù)式語言,當(dāng)函數(shù)記住并訪問所在詞法作用域的自由變量時(shí),就產(chǎn)生了閉包,即使函數(shù)是在當(dāng)前詞法作用域外執(zhí)行。
閉包在 JS 代碼中非常常見,不必把它想得太玄乎。
歡迎關(guān)注我的個(gè)人公眾號“謝南波”,專注分享原創(chuàng)文章。
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/98506.html
摘要:深入系列第八篇,介紹理論上的閉包和實(shí)踐上的閉包,以及從作用域鏈的角度解析經(jīng)典的閉包題。定義對閉包的定義為閉包是指那些能夠訪問自由變量的函數(shù)。 JavaScript深入系列第八篇,介紹理論上的閉包和實(shí)踐上的閉包,以及從作用域鏈的角度解析經(jīng)典的閉包題。 定義 MDN 對閉包的定義為: 閉包是指那些能夠訪問自由變量的函數(shù)。 那什么是自由變量呢? 自由變量是指在函數(shù)中使用的,但既不是函數(shù)參數(shù)也...
摘要:閉包面試題解由于作用域鏈機(jī)制的影響,閉包只能取得內(nèi)部函數(shù)的最后一個(gè)值,這引起的一個(gè)副作用就是如果內(nèi)部函數(shù)在一個(gè)循環(huán)中,那么變量的值始終為最后一個(gè)值。 (關(guān)注福利,關(guān)注本公眾號回復(fù)[資料]領(lǐng)取優(yōu)質(zhì)前端視頻,包括Vue、React、Node源碼和實(shí)戰(zhàn)、面試指導(dǎo)) 本周正式開始前端進(jìn)階的第二期,本周的主題是作用域閉包,今天是第8天。 本計(jì)劃一共28期,每期重點(diǎn)攻克一個(gè)面試重難點(diǎn),如果你還不了...
摘要:使用上一篇文章的例子來說明下自由變量進(jìn)階期深入淺出圖解作用域鏈和閉包訪問外部的今天是今天是其中既不是參數(shù),也不是局部變量,所以是自由變量。 (關(guān)注福利,關(guān)注本公眾號回復(fù)[資料]領(lǐng)取優(yōu)質(zhì)前端視頻,包括Vue、React、Node源碼和實(shí)戰(zhàn)、面試指導(dǎo)) 本周正式開始前端進(jìn)階的第二期,本周的主題是作用域閉包,今天是第7天。 本計(jì)劃一共28期,每期重點(diǎn)攻克一個(gè)面試重難點(diǎn),如果你還不了解本進(jìn)階計(jì)...
摘要:為了更好的理解,在閱讀此文之前建議先閱讀上一篇進(jìn)擊之詞法作用域與作用域鏈?zhǔn)裁词情]包閉包的含義就是閉合,包起來,簡單的來說,就是一個(gè)具有封閉功能與包裹功能的結(jié)構(gòu)。在中函數(shù)構(gòu)成閉包。 為了更好的理解,在閱讀此文之前建議先閱讀上一篇《進(jìn)擊JavaScript之詞法作用域與作用域鏈》 1.什么是閉包 閉包的含義就是閉合,包起來,簡單的來說,就是一個(gè)具有封閉功能與包裹功能的結(jié)構(gòu)。所謂的閉包就是...
摘要:當(dāng)面試中讓我解釋一下閉包時(shí)我懵逼了。這個(gè)解釋開始可能有點(diǎn)晦澀,讓我們抽絲剝繭摘下閉包的真面目。此文不詳述作用域有專門的主題闡述,不過作用域是理解閉包原理的基礎(chǔ)。這才是閉包的真正便利之處。閉包使用不當(dāng)就會(huì)很坑。 原文鏈接 為什么深度學(xué)習(xí)JavaScript? JavaScript如今是最流行的編程語言之一。它運(yùn)行在瀏覽器、服務(wù)器、移動(dòng)設(shè)備、桌面應(yīng)用,也可能包括冰箱。無需我舉其他再多不相干...
摘要:閉包確實(shí)是一個(gè)說爛了的概念,校招社招都會(huì)被問到,今天總結(jié)一番。先下定義,閉包是函數(shù)和該函數(shù)的詞法作用域的組合。在這個(gè)栗子里,函數(shù)以及它對變量的引用就構(gòu)成了閉包。閉包和作用域?qū)τ陂]包和作用域的關(guān)系,我的理解是閉包其實(shí)就是作用域的延伸。 閉包確實(shí)是一個(gè)說爛了的概念,校招社招都會(huì)被問到,今天總結(jié)一番。先下定義,閉包是函數(shù)和該函數(shù)的詞法作用域的組合。其實(shí)這個(gè)定義是比較教條的,可以直白的理解為閉...
閱讀 3313·2023-04-26 00:58
閱讀 1268·2021-09-22 16:04
閱讀 3311·2021-09-02 15:11
閱讀 1554·2019-08-30 15:55
閱讀 2339·2019-08-30 15:55
閱讀 3248·2019-08-23 18:41
閱讀 3458·2019-08-23 18:18
閱讀 2752·2019-08-23 17:53