詳解JavaScript作用域 閉包
JavaScript閉包,是JS開(kāi)發(fā)工程師必須深入了解的知識(shí)。3月份自己曾撰寫(xiě)博客《JavaScript閉包》,博客中只是簡(jiǎn)單闡述了閉包的工作過(guò)程和列舉了幾個(gè)示例,并沒(méi)有去刨根問(wèn)底,將其弄明白!
現(xiàn)在隨著對(duì)JavaScript更深入的了解,也剛讀完《你不知道的JavaScript(上卷)》這本書(shū),所以乘機(jī)整理下,從底層和原理上去刨一下。
JavaScript并不具有動(dòng)態(tài)作用域,它只有詞法作用域。詞法作用域是在寫(xiě)代碼或者說(shuō)定義時(shí)確定的,而動(dòng)態(tài)作用域是在運(yùn)行時(shí)確定的。了解閉包前,首先我們得知道什么是詞法作用域(作用域是由書(shū)寫(xiě)代碼時(shí)函數(shù)聲明的位置來(lái)決定的)。
一、何為閉包
示例1:
function foo(){ var a = 2; function bar(){ console.log(a); } return bar; } var baz = foo(); bzz(); //2
在foo()執(zhí)行后,通常認(rèn)為垃圾回收機(jī)制會(huì)將foo()的整個(gè)內(nèi)部作用域都被銷(xiāo)毀;而閉包可以阻止這樣事情發(fā)生,讓其內(nèi)部作用域依然存在。因?yàn)閎ar()處于foo()內(nèi)部,它擁有涵蓋foo()作用域的閉包,使得該作用域能夠一直存活,以供bar()在之后任何時(shí)間進(jìn)行引用。
bar()依然持有對(duì)該作用域的引用,而這個(gè)引用就叫作閉包。
簡(jiǎn)言之:當(dāng)函數(shù)可以記住并訪問(wèn)所在的詞法作用域,即使函數(shù)是在當(dāng)前詞法作用域之外執(zhí)行,這時(shí)就產(chǎn)生了閉包。
示例2:
無(wú)論使用何種方式對(duì)函數(shù)類(lèi)型的值進(jìn)行傳遞,當(dāng)函數(shù)在別處被調(diào)用時(shí)都可以觀察到閉包。
function foo(){ var a = 2; function baz(){ console.log(a); } bar(baz); } function bar(fn){ fn(); // 這就是閉包 }
示例3:
將一個(gè)內(nèi)部函數(shù)(timer)傳遞給setTimeout。timer具有涵蓋wait()作用域的閉包,保有對(duì)變量message的引用。
wait()執(zhí)行1000毫秒后,它的作用域并不會(huì)消失,timer依然保有wait()作用域的閉包。
function wait(message){ setTimeout( function timer(){ console.log(message); },1000); } wait("Hello,ligang");
示例4:
下述activator()具有涵蓋setupBot()作用域的閉包!
function setupBot(name, selector){ $(selector).click(function activator(){ console.log("Activating: "+ name); }); } setupBot("Closure Bot 1", "#bot_1"); setupBot("Closure Bot 2", "#bot_2");
二、循環(huán)和閉包
for(var i=1; i<=5; i++){ setTimeout(function timer(){ console.log(i); }, i*1000); } // 期望:每秒一次的頻率輸出1~5 // 結(jié)果:每秒一次的頻率輸出五次6
先解釋一下:“i*1000”,5個(gè)定時(shí)分別在1s、2s、3s、4s、5s后執(zhí)行,并不是1s、3s、6s、10s、15s。也就是頻率為1s,不是每次間隔增加1s。如果去掉i寫(xiě)成“1000”,會(huì)在for執(zhí)行完1s后直接輸出五次6。
回調(diào)函數(shù)在循環(huán)結(jié)束后才被執(zhí)行,因此輸出的是循環(huán)終止條件是i值。事實(shí)上,當(dāng)定時(shí)器運(yùn)行時(shí)即使每個(gè)迭代中執(zhí)行的是setTimeout(..., 0),所有的回調(diào)函數(shù)依然是在循環(huán)結(jié)束后才被執(zhí)行。
根據(jù)作用域的工作原理,盡管五個(gè)函數(shù)是在各個(gè)迭代中分別定義的,但是它們都被封閉在一個(gè)共享的全局作用域中,因此實(shí)際上只有一個(gè)i。
解決方案1:
for(var i=0; i<=5; i++){ (function(j){ setTimeout(function timer(){ console.log(j); }, j*1000 ); })(i); } // 結(jié)果:每秒一次的頻率輸出1~5
每個(gè)迭代都生成一個(gè)新的作用域,使得延遲函數(shù)的回調(diào)可以將新的作用封閉在每個(gè)迭代內(nèi)部,每個(gè)迭代中都會(huì)含有一個(gè)具有正確值的變量供我們?cè)L問(wèn)。
解決方案2(ES6):
for(var i=0; i<=5; i++){ let j = i; setTimeout(function timer(){ console.log(j); }, j*1000 ); } // 結(jié)果:每秒一次的頻率輸出1~5 for(let i=0; i<=5; i++){ setTimeout(function timer(){ console.log(i); }, i*1000 ); } // 結(jié)果:每秒一次的頻率輸出五次6
三、模塊
模塊需要具備兩個(gè)必要條件:
(1)必須有外部的封閉函數(shù),該函數(shù)必須至少被調(diào)用一次(每次調(diào)用都會(huì)創(chuàng)建一個(gè)新的模塊實(shí)例)。
(2)封閉函數(shù)必須返回至少一個(gè)內(nèi)部函數(shù),這樣內(nèi)部函數(shù)才能在私有作用域中形成閉包,并且可以訪問(wèn)或者修改私有的狀態(tài)。
典型的模塊化:
function CoolMoudle(){ var something = "cool"; var doSomething = function(){ console.log(something); } return{ doSomething: doSomething }; } var foo = CoolMoudle(); //如果不執(zhí)行外部函數(shù)CoolMoudle(),內(nèi)部作用域和閉包都無(wú)法創(chuàng)建 foo.doSomething(); //cool
單例模式:
var foo = (function CoolModule(id){ function change(){ // 修改公共API publicAPI.identify = identify2; } function identify1(){ console.log(id); } function identify2(){ console.log(id.toUpperCase()); } var publicAPI = { change: change, identify: identify1 }; return publicAPI; })("foo module"); foo.identify(); //foo module foo.change(); foo.identify(); //FOO MODULE
以上就是詳解JavaScript作用域 閉包的詳細(xì)內(nèi)容,更多關(guān)于JavaScript作用域 閉包的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
js利用數(shù)組length屬性清空和截短數(shù)組的小例子
這篇文章主要介紹了js利用數(shù)組length屬性清空和截短數(shù)組的小例子,有需要的朋友可以參考一下2014-01-01一文讀懂JS中的var/let/const和暫時(shí)性死區(qū)
這篇文章主要為大家詳細(xì)介紹了JavaScript中的var、let、const和暫時(shí)性死區(qū)的異同,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以了解一下2023-02-02JS使用正則表達(dá)式過(guò)濾多個(gè)詞語(yǔ)并替換為相同長(zhǎng)度星號(hào)的方法
這篇文章主要介紹了JS使用正則表達(dá)式過(guò)濾多個(gè)詞語(yǔ)并替換為相同長(zhǎng)度星號(hào)的方法,涉及javascript字符串與正則替換操作相關(guān)技巧,需要的朋友可以參考下2016-08-08JS控制靜態(tài)頁(yè)面?zhèn)鬟f參數(shù)并獲取參數(shù)應(yīng)用
這篇文章主要介紹了JS控制靜態(tài)頁(yè)面?zhèn)鬟f參數(shù)并獲取參數(shù)應(yīng)用的相關(guān)資料,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友可以參考下2016-08-08js數(shù)據(jù)類(lèi)型以及其判斷方法實(shí)例
這篇文章主要給大家介紹了關(guān)于js數(shù)據(jù)類(lèi)型以及其判斷方法的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2021-03-03純css實(shí)現(xiàn)窗戶(hù)玻璃雨滴逼真效果
css實(shí)現(xiàn)窗戶(hù)上水珠效果,效果特別逼真,窗外的雨淅淅瀝瀝飄打在玻璃上,看起來(lái)很像模糊的窗外,斜滴的雨露,接下來(lái)給大家一起來(lái)用CSS技術(shù)實(shí)現(xiàn)這樣一幅畫(huà)面,下面給大家分享使用純css實(shí)現(xiàn)窗戶(hù)玻璃雨滴逼真效果,感興趣的朋友快樂(lè)圍觀吧2015-08-08uni-app打開(kāi)外部鏈接方式總結(jié)(h5和app)
uni-app在app和h5中跳轉(zhuǎn)至外部鏈接的方式有所不同,需要進(jìn)行條件編譯,下面這篇文章主要給大家介紹了關(guān)于uni-app打開(kāi)外部鏈接方式(h5和app)的相關(guān)資料,文章通過(guò)實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下2023-02-02JavaScript基礎(chǔ)知識(shí)及常用方法總結(jié)
JAVASCRIPT是AJAX技術(shù)中不可或缺的一部分,所以想學(xué)好AJAX以及現(xiàn)在流行的AJAX框架,學(xué)好JAVASCRIPT是最重要的,通過(guò)本篇文章給大家介紹javascript基礎(chǔ)知識(shí)及常用方法總結(jié),對(duì)js基礎(chǔ)知識(shí)及常用方法相關(guān)知識(shí)感興趣的朋友一起學(xué)習(xí)吧2016-01-01