JavaScript範圍閉包(你不知道的JavaScript)

來源:互聯網
上載者:User

標籤:javascript範圍閉包   js閉包   你不知道的javascript   js模組   

JavaScript閉包,是JS開發工程師必須深入瞭解的知識。3月份自己曾撰寫部落格《JavaScript閉包》,部落格中只是簡單闡述了閉包的工作過程和列舉了幾個樣本,並沒有去刨根問底,將其弄明白!

現在隨著對JavaScript更深入的瞭解,也剛讀完《 你不知道的JavaScript(上卷)》這本書,所以乘機整理下,從底層和原理上去刨一下。

JavaScript並不具有動態範圍,它只有詞法範圍。詞法範圍是在寫代碼或者說定義時確定的,而動態範圍是在運行時確定的。瞭解閉包前,首先我們得知道什麼是詞法範圍(範圍是由書寫代碼時函式宣告的位置來決定的)。
一、何為閉包樣本1:
function foo(){var a = 2;function bar(){console.log(a);}return bar;}var baz = foo();bzz(); //2
在foo()執行後,通常認為記憶體回收機制會將foo()的整個內部範圍都被銷毀;而閉包可以阻止這樣事情發生,讓其內部範圍依然存在。因為bar()處於foo()內部,它擁有涵蓋foo()範圍的閉包,使得該範圍能夠一直存活,以供bar()在之後任何時間進行引用。

bar()依然持有對該範圍的引用,而這個引用就叫作 閉包
簡言之:當函數可以記住並訪問所在的詞法範圍,即使函數是在當前詞法範圍之外執行,這時就產生了閉包。
樣本2:無論使用何種方式對函數類型的值進行傳遞,當函數在別處被調用時都可以觀察到閉包。
function foo(){var a = 2;function baz(){console.log(a);}bar(baz);}function bar(fn){fn();// 這就是閉包}
樣本3:將一個內建函式(timer)傳遞給setTimeout。timer具有涵蓋wait()範圍的閉包,保有對變數message的引用。
wait()執行1000毫秒後,它的範圍並不會消失,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");
二、迴圈和閉包
for(var i=1; i<=5; i++){setTimeout(function timer(){console.log(i);}, i*1000);}// 期望:每秒一次的頻率輸出1~5// 結果:每秒一次的頻率輸出五次6
先解釋一下:“i*1000”,5個定時分別在1s、2s、3s、4s、5s後執行,並不是1s、3s、6s、10s、15s。也就是頻率為1s,不是每次間隔增加1s。如果去掉i寫成“1000”,會在for執行完1s後直接輸出五次6。

回呼函數在迴圈結束後才被執行,因此輸出的是迴圈終止條件是i值。事實上,當定時器運行時即使每個迭代中執行的是setTimeout(..., 0),所有的回呼函數依然是在迴圈結束後才被執行。
根據範圍的工作原理,儘管五個函數是在各個迭代中分別定義的,但是它們都被封閉在一個共用的全域範圍中,因此實際上只有一個i。
解決方案1:
for(var i=0; i<=5; i++){(function(j){setTimeout(function timer(){console.log(j);}, j*1000 );})(i);}// 結果:每秒一次的頻率輸出1~5
每個迭代都產生一個新的範圍,使得延遲函數的回調可以將新的作用封閉在每個迭代內部,每個迭代中都會含有一個具有正確值的變數供我們訪問。
解決方案2(ES6):
for(var i=0; i<=5; i++){let j = i;setTimeout(function timer(){console.log(j);}, j*1000 );}// 結果:每秒一次的頻率輸出1~5


for(let i=0; i<=5; i++){setTimeout(function timer(){console.log(i);}, i*1000 );}// 結果:每秒一次的頻率輸出五次6

三、模組

模組需要具備兩個必要條件:

(1)必須有外部的封閉函數,該函數必須至少被調用一次(每次調用都會建立一個新的模組執行個體)。

(2)封閉函數必須返回至少一個內建函式,這樣內建函式才能在私人範圍中形成閉包,並且可以訪問或者修改私人的狀態。

典型的模組化:
function CoolMoudle(){var something = "cool";var doSomething = function(){console.log(something);}return{doSomething: doSomething};}var foo = CoolMoudle();//如果不執行外部函數CoolMoudle(),內部範圍和閉包都無法建立foo.doSomething();//cool
單例模式:
var foo = (function CoolModule(id){function change(){// 修改公用APIpublicAPI.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 modulefoo.change();foo.identify(); //FOO MODULE

著作權聲明:本文為博主原創文章,未經博主允許不得轉載。

JavaScript範圍閉包(你不知道的JavaScript)

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.