小編給大家分享一下JavaScript中閉包的案例分析,相信大部分人都還不怎么了解,因此分享這篇文章給大家參考一下,希望大家閱讀完這篇文章后大有收獲,下面讓我們一起去了解一下吧!

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