javascript
JS闭包—你不知道的JavaScript上卷读书笔记(二)
關于閉包,初學者會被繞的暈頭轉向,在學習的路上也付出了很多精力來理解。 讓我們一起來揭開閉包神秘的面紗。
閉包晦澀的定義
看過很多關于閉包的定義,很多講的云里霧里,晦澀難懂。讓不少人以為閉包是多么玄乎的東西。在我看過的所有書籍中,我更喜歡《你不知道的javascript(上卷)》的定義:
當函數可以記住并訪問所在的詞法作用域時,就產生了閉包,或者說函數在創建時的詞法作域之外執行。
通俗的來說(不嚴謹): 就是函數套函數,子函數可以有權訪問父函數的變量、父函數的父函數的變量、一直到全局變量。子函數如果不被銷毀,整條作用域鏈上的變量仍然保存在內存中。
關于詞法作用域,這里不做過多的解釋,詳情參考:http://www.cnblogs.com/ylweb/p/7531259.html.
下面用一些代碼來解釋這個定義:
function foo() {var a = 2;function bar() {console.log( a ); // 2}bar(); }foo();嚴格來說這段代碼并沒有形成閉包,因為bar是在創建時所在的詞法作用域執行。bar() 對a 的引用的方法是詞法作用域的查找規則,而這些規則只是閉包的一部分。從學術的角度說:,在上面的代碼片段中,函數bar() 具有一個涵蓋foo() 作用域的閉包
(事實上,涵蓋了它能訪問的所有作用域,比如全局作用域)。也可以認為bar() 被封閉在
了foo() 的作用域中。為什么呢?原因簡單明了,因為bar() 嵌套在foo() 內部。
下面我們來看一段代碼,清晰地展示了閉包:
function foo() {var a = 2;function bar() {console.log( a );}return bar; }var baz = foo(); baz(); // 2 —— 朋友,這就是閉包的效果。函數bar() 的詞法作用域能夠訪問foo() 的內部作用域。然后我們將bar() 函數本身當作一個值類型進行傳遞。在這個例子中,我們將bar 所引用的函數對象本身當作返回值。
在foo() 執行后,其返回值(也就是內部的bar() 函數)賦值給變量baz 并調用baz(),實際上只是通過不同的標識符引用調用了內部的函數bar()。
bar() 顯然可以被正常執行。但是在這個例子中,它在自己定義的詞法作用域以外的地方執行。
在foo() 執行后,通常會期待foo() 的整個內部作用域都被銷毀,因為我們知道引擎有垃圾回收器用來釋放不再使用的內存空間。由于看上去foo() 的內容不會再被使用,所以很自然地會考慮對其進行回收。
而閉包的“神奇”之處正是可以阻止這件事情的發生。事實上內部作用域依然存在,因此沒有被回收。誰在使用這個內部作用域?原來是bar() 本身在使用。
拜bar() 所聲明的位置所賜,它擁有涵蓋foo() 內部作用域的閉包,使得該作用域能夠一直存活,以供bar() 在之后任何時間進行引用。
bar() 依然持有對該作用域的引用,而這個引用就叫作閉包。
循環與閉包
以下是一個最常見的for循環例子:
for (var i=1; i<=5; i++) {setTimeout( function timer() {console.log( i );}, i*1000 ); }正常情況下,我們對這段代碼行為的預期是分別輸出數字1~5,但實際上,這段代碼在運行時輸出五次6。 Why?
首先解釋6 是從哪里來的。這個循環的終止條件是i 不再<=5。條件首次成立時i 的值是6。因此,輸出顯示的是循環結束時i 的最終值。
仔細想一下,這好像又是顯而易見的,延遲函數的回調會在循環結束時才執行。事實上,當定時器運行時即使每個迭代中執行的是setTimeout(.., 0),所有的回調函數依然是在循環結束后才會被執行,因此會每次輸出一個6 出來。
問題實質:
我們試圖假設循環中的每個迭代在運行時都會給自己“捕獲”一個i 的副本。但是根據作用域的工作原理,實際情況是盡管循環中的五個函數是在各個迭代中分別定義的,但是它們都被封閉在一個共享的全局作用域中,因此實際上只有一個i。
改進方案:
for (var i=1; i<=5; i++) {(function(j) {setTimeout( function timer() {console.log( j );}, j*1000 );})( i ); }ES6改進:
for (let i=1; i<=5; i++) {setTimeout( function timer() {console.log( i );}, i*1000 ); }轉載于:https://www.cnblogs.com/ylweb/p/7804309.html
總結
以上是生活随笔為你收集整理的JS闭包—你不知道的JavaScript上卷读书笔记(二)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 复习……方法的重载
- 下一篇: DNS A记录和CNAME记录