小編給大家分享一下JavaScript中閉包的案例分析,相信大部分人都還不怎么了解,因此分享這篇文章給大家參考一下,希望大家閱讀完這篇文章后大有收獲,下面讓我們一起去了解一下吧!
成都創(chuàng)新互聯(lián)公司主營沿灘網(wǎng)站建設(shè)的網(wǎng)絡(luò)公司,主營網(wǎng)站建設(shè)方案,重慶APP軟件開發(fā),沿灘h5重慶小程序開發(fā)搭建,沿灘網(wǎng)站營銷推廣歡迎沿灘等地區(qū)企業(yè)咨詢想要深入學(xué)習(xí)JavaScript這門語言,閉包
這個概念幾乎是繞不開的關(guān)鍵,今天就讓我們一起好好地盤一盤,閉包
到底是什么東西。
如果是零基礎(chǔ)的小伙伴,可以先看看前一篇文章,幫助你更好的理解本文的內(nèi)容:
我們先來看看閉包在MDN中的定義:
一個函數(shù)和對其周圍狀態(tài)(lexical environment,詞法環(huán)境)的引用捆綁在一起(或者說函數(shù)被引用包圍),這樣的組合就是閉包(closure)
閉包的定義非?;逎y懂,如果通俗的解釋一下的話,就是:
這么講可能還是非常抽象,不過我們先有個概念,下面會詳細(xì)解釋這個概念。對于閉包,其實(shí)還有另一種解釋:
區(qū)別于第一種解釋,第二種解釋把函數(shù)本身稱作為閉包,這樣的解釋也合理,但更準(zhǔn)確的話其實(shí)我們可以把這個函數(shù)稱作閉包函數(shù)
。這樣的話,其實(shí)兩種解釋都沒有問題,也可以同時存在了。
看完了閉包的概念,我們通過實(shí)際的例子感受一下,閉包長什么樣?閉包產(chǎn)生的條件?
function outer(){ var a=0; function inner(){ console.log(++a); } return inner; }var f=outer(); f(); //1f(); //2復(fù)制代碼
我們先來看下這個例子里,閉包是如何體現(xiàn)的。我們首先定義了outer
函數(shù),然后在outer
函數(shù)的內(nèi)部聲明了一個inner
函數(shù)并返回。
在外部,我們執(zhí)行outer
函數(shù),并將返回的inner
函數(shù)保存在了f
中,現(xiàn)在的f
就是我們說的閉包函數(shù)
了。
在例子中,我們執(zhí)行了兩次f
函數(shù),分別輸出了1
和2
,說明我們在執(zhí)行f
函數(shù)中的console.log(++a);
語句時,根據(jù)我們之前對作用域的解釋,會去函數(shù)所在的作用域鏈上尋找變量,最終在outer
函數(shù)中找到了這個變量,并進(jìn)行了增值操作。
同時需要注意的是,我們輸出的結(jié)果并不是兩次1
,說明我們的作用域是共享的,可以看成outer
的作用域延伸到了外部。
outer
作用域本來應(yīng)該在執(zhí)行結(jié)束后釋放,但根據(jù)GC
機(jī)制(我們這里先不展開介紹)會發(fā)現(xiàn):
outer
了,但a
變量好像被引用了,而且外部還保存了起來不知道什么時候調(diào)用?好叭,那我就先不釋放了。閉包就這樣誕生了,在作用域消失之后,外部依然可以訪問作用域中的變量。
我們根據(jù)剛剛的例子和解釋總結(jié)一下,產(chǎn)生一個閉包需要哪些條件:
outer
就是外部函數(shù),inner
就是返回的內(nèi)部函數(shù)。inner
函數(shù)就引用了變量a
。outer
需要被執(zhí)行,同時需要把返回的內(nèi)部函數(shù)給保存起來,也就是我們例子中的var f=outer()
。只有同時滿足了這三個條件,才能產(chǎn)生閉包??此坪茈y達(dá)成,事實(shí)上,我們實(shí)際代碼中經(jīng)常不經(jīng)意間就能產(chǎn)生閉包。
通過剛剛的介紹,相信大家應(yīng)該對閉包有了一個概念,接下來就讓我們出發(fā),深入的了解一下閉包的方方面面。
在剛剛的例子中,我們創(chuàng)建了一個閉包,執(zhí)行多次閉包函數(shù)后,增加的都是同一個作用域中的變量a
,那么我們試試創(chuàng)建多個閉包會怎么樣:
function outer() { var a = 0; function inner() { console.log(++a); } return inner; }var f1 = outer();var f2 = outer(); f1(); //1f2(); //1復(fù)制代碼
這段代碼在剛剛的例子上進(jìn)行了改動,我們執(zhí)行了兩次外部函數(shù)outer
,并分別用不同的變量f1
和f2
保存。當(dāng)執(zhí)行f1
和f2
時會發(fā)現(xiàn),輸出的結(jié)果都是1
,說明f1
和f2
的作用域是獨(dú)立的,f1
和f2
屬于兩個不同的閉包,我們用一張圖來理解下:
當(dāng)分別創(chuàng)建f1
和f2
時,調(diào)用了兩次outer
函數(shù),創(chuàng)建了兩個不同的上下文。而當(dāng)f1
和f2
分別執(zhí)行時,根據(jù)作用域的查找規(guī)則,會去對應(yīng)的作用域中查找變量,并執(zhí)行增值輸出,所以最終兩個值均為2
;
我們知道,作用域的外部無法拿到作用域內(nèi)部的值,而通過閉包,我們可以把作用域我們需要的值或者方法暴露出去,比如:
function outer() { var myNumber = 0; function addNumber() { myNumber++; } function getNumber() { return myNumber; } return { addNumber, getNumber } }var module = outer();module.addNumber();module.addNumber();module.getNumber();復(fù)制代碼
在這個例子中,我們同樣定義了一個外部函數(shù)outer
,另外還分別定義了兩個函數(shù)addNumber
和getNumber
,用于增加和獲取變量myNumber
。
當(dāng)我們執(zhí)行outer()
語句的時候,會創(chuàng)建一個上下文,同時把內(nèi)部返回的對象保存在module
變量上,此時我們就創(chuàng)建了一個閉包,以及一個包含方法addNumber
和getNumber
的對象。
由于外部是無法直接訪問變量myNumber
的,但由于閉包的原因,addNumber
和getNumber
是可以訪問到這個變量的,因此我們成功的把變量myNumber
隱藏了起來,并且對外只提供了增加和獲取myNumber
值的方法。
通過剛剛的例子,相信大家應(yīng)該對閉包有了一定了解,接下來我們試著運(yùn)用閉包來解決實(shí)際問題,先看一下例子:
for (var i = 0; i < 2; i++) { setTimeout(() => { console.log(i); }, 500); }復(fù)制代碼
這是一個十分容易令人誤解的例子。接觸過的小伙伴肯定都知道,最后會輸出兩次2
而不是依次輸出0
和1
,我們來看看為什么會這樣。
首先,外部是一個for
循環(huán),執(zhí)行了兩次語句。
for (var i = 0; i < 2; i++) { ... // 執(zhí)行兩次}復(fù)制代碼
在函數(shù)的內(nèi)部,我們調(diào)用了setTimeout
函數(shù),關(guān)鍵的地方來了,這個函數(shù)是一個異步函數(shù),并不會馬上執(zhí)行,所以實(shí)際上等外部的for
循環(huán)執(zhí)行結(jié)束了,才會真的執(zhí)行setTimeout
中的函數(shù)。還有第二個關(guān)鍵點(diǎn)在于,在for
循環(huán)中,var
定義的變量相當(dāng)于定義在全局,而不存在塊級作用域。那么剛剛的代碼就可以近似的看成這樣了。
var i=0; i++; //i=1i++; //i=2console.log(i);console.log(i);復(fù)制代碼
非常直接粗暴,但可以很清晰的看出輸出結(jié)果為何是兩次2
了,因?yàn)榇蠹夜灿昧送粋€作用域,i
的值被覆蓋了。那么知道了問題出在哪里,我們試著用上我們剛剛學(xué)習(xí)的閉包,來創(chuàng)建不同的作用域:
for (var i = 0; i < 2; i++) { function outer() { var j = i; setTimeout(function inner() { console.log(j); }, 500); } outer(); }復(fù)制代碼
我們按照閉包的樣式對剛剛的代碼進(jìn)行了改造,這里的setTimeout
并不直接就是inner
函數(shù),這是因?yàn)樗谶@里起到了定義inner
函數(shù),并保存執(zhí)行inner
函數(shù)的功能。
我們可以看到,最終結(jié)果依次輸出了0
和1
,說明我們的閉包是成功了的,但這樣的閉包比較臃腫,我們試著提高一下,寫的更加優(yōu)雅一點(diǎn):
for (var i = 0; i < 2; i++) { (function() { //用自執(zhí)行函數(shù)代替了`outer`函數(shù)的定義和執(zhí)行兩個步驟 var j = i; setTimeout(function inner() { console.log(j); }, 500); })(); }復(fù)制代碼
還可以再簡化一下:
for (var i = 0; i < 5; i++) { for (var i = 0; i < 2; i++) { (function(j) {//用自執(zhí)行函數(shù)代替了`outer`函數(shù)的定義和執(zhí)行兩個步驟 setTimeout(function inner() { console.log(j); }, 500); })(i);//var j=i的步驟,通過傳入i值替換 } }復(fù)制代碼
這樣就大功告成了!
以上是JavaScript中閉包的案例分析的所有內(nèi)容,感謝各位的閱讀!相信大家都有了一定的了解,希望分享的內(nèi)容對大家有所幫助,如果還想學(xué)習(xí)更多知識,歡迎關(guān)注創(chuàng)新互聯(lián)-成都網(wǎng)站建設(shè)公司行業(yè)資訊頻道!
本文標(biāo)題:JavaScript中閉包的案例分析-創(chuàng)新互聯(lián)
轉(zhuǎn)載來于:http://muchs.cn/article4/phsoe.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供建站公司、網(wǎng)站建設(shè)、標(biāo)簽優(yōu)化、網(wǎng)頁設(shè)計(jì)公司、電子商務(wù)、網(wǎng)站改版
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)