JavaScript-閉包

閉包(Closure)概念

在A函數(shù)中定義了一個(gè)B函數(shù),在B函數(shù)中使用了A函數(shù)中的變量,就會(huì)產(chǎn)生閉包,其中B就是一個(gè)閉包。
也可以說,定義在一個(gè)函數(shù)內(nèi)部的這個(gè)函數(shù)就是閉包。

理解閉包需要的幾個(gè)相關(guān)概念

1.變量的作用域

在ES5中變量有兩個(gè)作用域:

  1. 全局作用域(global)
  2. 局部作用域(local)

在ES6中補(bǔ)充了一個(gè)新的作用域-塊級(jí)作用域(block)

當(dāng)定義變量的地方?jīng)]有被 function 包括則是全局變量,否則就是局部變量。

在函數(shù)的內(nèi)部會(huì)優(yōu)先使用局部變量(即使全局變量和局部變量同名亦是如此),在函數(shù)調(diào)用的過程,會(huì)給局部變量創(chuàng)建一個(gè)函數(shù)棧區(qū)(區(qū)別于全局棧),來保存這些局部變量。正常情況下在函數(shù)調(diào)用結(jié)束后,函數(shù)棧會(huì)被垃圾回收機(jī)制處理。

2.執(zhí)行上下文

  • 代碼的運(yùn)行會(huì)產(chǎn)生執(zhí)行上下文,如果代碼不運(yùn)行就沒有。
  • 全局代碼產(chǎn)生全局上下文
  • 函數(shù)代碼產(chǎn)生函數(shù)上下文
  • 函數(shù)嵌套調(diào)用形成執(zhí)行上下文棧
  • 執(zhí)行上下文中保存了執(zhí)行代碼所需要的各類的數(shù)據(jù)
  • 執(zhí)行上下文是一個(gè)對(duì)象,在代碼運(yùn)行過程中產(chǎn)生,代碼運(yùn)行完成后就消失.

執(zhí)行上下文的組成

  1. 自己的執(zhí)行上下文
  2. 父級(jí)函數(shù)的執(zhí)行上下文

全局上下文

一旦<script></script>標(biāo)簽中代碼運(yùn)行起來,就會(huì)產(chǎn)生一個(gè)執(zhí)行上下文,這個(gè)執(zhí)行上下文就是全局執(zhí)行上下文.

  • 全局上下文只有一個(gè)
  • 全局執(zhí)行環(huán)境是window對(duì)象,所有變量和函數(shù)都作為window對(duì)象的屬性和方法創(chuàng)建的
  • 所有的代碼都在全局執(zhí)行上下文中執(zhí)行

函數(shù)執(zhí)行上下文

每次調(diào)用函數(shù)都會(huì)產(chǎn)生一個(gè)執(zhí)行上下文,函數(shù)調(diào)用完成后,會(huì)把執(zhí)行上下文釋放掉。
按照函數(shù)的調(diào)用順序,這些上下文以棧的形式存儲(chǔ)(先進(jìn)后出).棧底是全局上下文。

3.詞法作用域-靜態(tài)作用域

js代碼的書寫順序,就決定了變量的作用域。換言之,在函數(shù)內(nèi)部去訪問一個(gè)變量,應(yīng)該去定義這個(gè)函數(shù)(寫這個(gè)函數(shù)的位置)的相關(guān)作用域中去找,而不是調(diào)用這個(gè)函數(shù)的那個(gè)作用域中去找。

舉個(gè)栗子就比較好懂些,如下圖

4.函數(shù)的嵌套定義

在JS中,在函數(shù)體中可以再次定義另一個(gè)函數(shù);并且可以多層函數(shù)嵌套使用。

5.作用域鏈

在JS中有兩條鏈,分別是作用域鏈和原型鏈,這里要著重說到作用域鏈。
在函數(shù)的內(nèi)部,要確定一個(gè)變量的值,會(huì)從當(dāng)前的作用域出發(fā),沿著作用域鏈向上找,如果找到全局作用域中還是沒有找到,那么就會(huì)報(bào)引用類型錯(cuò)誤。

再次理解閉包

從閉包的定義可以抓住兩個(gè)關(guān)鍵點(diǎn):

  • 函數(shù)嵌套定義
  • 引用變量

通過前面的幾個(gè)相關(guān)知識(shí)點(diǎn),可以發(fā)現(xiàn)在JS中,函數(shù)內(nèi)部可以通過作用域鏈機(jī)制輕松得到父級(jí)函數(shù)內(nèi)的變量乃至全局變量,而反過來則是行不通的,即函數(shù)外部是無法讀取函數(shù)內(nèi)局部變量的。

那么如果有的場(chǎng)景必須要得到函數(shù)內(nèi)部的局部變量時(shí),需要變通方法那么就產(chǎn)生了閉包。

可以在本質(zhì)上去理解閉包,閉包就是將函數(shù)內(nèi)部和函數(shù)外部連接起來的一座橋梁,可以使局部變量被外部函數(shù)訪問到,也就是說變相的延長了函數(shù)中局部變量的壽命。

上段代碼

function f(){  
    var a = 1;
    function f1(){
        console.log(a);
    }
    return f1;
} 
var r = f();
r();

這段代碼執(zhí)行后,結(jié)果輸出為1。也就是說當(dāng)函數(shù)f調(diào)用結(jié)束后,它的局部變量a并沒有被回收掉。可以說這就是閉包的本質(zhì),它使得函數(shù)調(diào)用結(jié)束后,被閉包引用的變量沒有被回收機(jī)制干掉而順利存活了下來,還可以被外部訪問和使用。

閉包的作用

根據(jù)前面的理解,可以歸納閉包的作用有:

  1. 讀取函數(shù)內(nèi)部的變量
  2. 延長這些變量的生命周期

使用閉包的栗子來一個(gè) - 節(jié)流函數(shù)

節(jié)流函數(shù),可以讓一個(gè)函數(shù)變得"懶",調(diào)用一次之后需要隔一段時(shí)間才能再次調(diào)用,即降低函數(shù)的可被調(diào)用的頻率。話不多說上代碼

上面的代碼中,f1就是被節(jié)流函數(shù)變懶了的test。
代碼運(yùn)行中,f函數(shù)中t1這個(gè)局部變量被f函數(shù)內(nèi)部定義的t函數(shù)引用后,當(dāng)f函數(shù)調(diào)用執(zhí)行完畢后,t1這個(gè)變量并沒有隨之被回收,而是一直可以被訪問。這就是閉包的體現(xiàn)。

閉包的弊端

  1. 閉包會(huì)使函數(shù)內(nèi)的變量一直被保存在內(nèi)存中, 這是極耗內(nèi)存的方式。不可以濫用閉包,會(huì)對(duì)網(wǎng)頁的性能有很大的影響。在IE中可能會(huì)導(dǎo)致內(nèi)存泄露。
  2. 閉包在函數(shù)外部可以訪問并改變函數(shù)內(nèi)部變量的值,這是好事也是壞事。 如果把父級(jí)函數(shù)作為對(duì)象使用,而把閉包作為它的公用方法,而又把其內(nèi)部變量作為它的私有屬性,這時(shí)就一定要注意了,不要輕易改變父級(jí)函數(shù)內(nèi)部變量的值。

以上就是對(duì)JavaScript中閉包的復(fù)習(xí)??

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 閉包(closure)是Javascript語言的一個(gè)難點(diǎn),也是它的特色,很多高級(jí)應(yīng)用都要依靠閉包實(shí)現(xiàn)。 一、變量...
    zouCode閱讀 1,358評(píng)論 0 13
  • javascript之閉包 閉包的概念 ????閉包(closure)是 JavaScript 的一種語法特性。 ...
    呦_小宋啊閱讀 235評(píng)論 0 2
  • 目錄 1.執(zhí)行環(huán)境與作用域鏈 2. 立即執(zhí)行函數(shù) 3. 閉包知識(shí)點(diǎn) 3.1 什么是閉包 3.2 使用閉包的意義與注...
    犯迷糊的小羊閱讀 696評(píng)論 0 11
  • 前言 這篇文章使用有效的javascript代碼向程序員們解釋了閉包,大牛和功能型程序員請(qǐng)自行忽略。 基礎(chǔ)篇 閉包...
    kiaizi閱讀 404評(píng)論 0 7
  • 前言 總括 :這篇文章使用有效的javascript代碼向程序員們解釋了閉包,大牛和功能型程序員請(qǐng)自行忽略。 譯者...
    KX九五閱讀 324評(píng)論 0 1

友情鏈接更多精彩內(nèi)容