2012-04-10 33 views
14

除了標準之外,還有Javascript中變量捕獲的確切來源(閱讀標準很痛苦)嗎?通過JavaScript中的閉包瞭解變量捕獲/節點

在下面的代碼i由值複製:

for (var i = 0; i < 10; i++) 
{ 
    (function (i) 
    { 
     process.nextTick(function() 
     { 
      console.log(i) 
     }) 
    }) (i) 
} 

所以它打印1..10。 process.nextTick是節點中的setTimeout(f,0)的模擬。

但在接下來的代碼中,我似乎並沒有被複制:

for (var i = 0; i < 10; i++) 
{ 
     var j = i 
     process.nextTick(function() 
     { 
      console.log(j) 
     }) 
} 

它打印9的10倍。爲什麼?我對參考文獻/一般文章更感興趣,而不是解釋這個具體的捕獲案例。

回答

6

我沒有方便的參考。但底線是:首先,你明確地將i傳遞給一個匿名函數,它創建一個新的範圍。您在第二次創建新作用域的範圍不是ij。而且,JavaScript總是捕獲變量,而不是值。所以你也可以修改我。

JavaScript var關鍵字具有函數作用域,而不是作用域作用域。所以for循環不會創建一個範圍。

作爲說明,非標準let關鍵字具有本地範圍。

+0

爲什麼我不是對於j – nponeccop 2012-04-10 19:52:22

+0

@nponeccop創建一個新的範圍目前尚不清楚,JavaScript有功能範圍。 – 2012-04-10 19:54:18

+0

我正把頭撞在桌子上。不知道,假設它是C++或Perl或Haskell :)迷人 – nponeccop 2012-04-10 19:58:36

4

它被複制(或分配)在你的第二個例子中,它只是只有一個副本的變量j,它將具有它最後的值,它將是9(for循環的最後一個版本)。您需要一個新的函數閉包爲for循環的每個rev創建一個變量的新副本。你的第二個例子只有一個變量,它對於你的for循環的所有轉速是共同的,因此它只能有一個值。

我不知道有關這個主題的任何明確的寫法。

javascript中的變量作用域爲函數級別。在javascript中沒有塊範圍。因此,如果您想爲for循環的每個rev創建一個變量的新版本,則必須使用一個新函數(創建函數閉包),以便每次通過for循環捕獲該新值。如果沒有函數閉包,那麼這個變量只有一個值,這個值對於那個變量的所有用戶來說都是通用的。

當你聲明一個變量,如您var j = i;在比函數開始,JavaScript的吊定義的函數的頂部和你的代碼變得等同於這個其他一些位置:

var j; 
for (var i = 0; i < 10; i++) 
{ 
     j = i; 
     process.nextTick(function() 
     { 
      console.log(j) 
     }) 
} 

這被稱爲variable hoisting,如果你想了解更多關於它的信息,你可以使用Google這個術語。但是,重點是隻有函數作用域,因此在函數的任何位置聲明的變量實際上會在函數的頂部聲明一次,然後分配給函數中的任何位置。

+0

更新我的答案,更詳細地介紹你的情況。 – jfriend00 2012-04-10 19:57:14

4

在JavaScript中,函數包含變量,這些變量在自己範圍之外的範圍內定義,使得它們對變量具有「活躍」引用,而不是在任何特定時間對其值的快照。

所以在你的第二個例子,你創建10個匿名函數(在process.nextTick(function(){...}))其中包圍變量j(和i,它總是有當創建匿名函數的值相同)。這些函數中的每一個在之後的時間處使用j的值,外部for循環已完全運行,因此在調用每個函數時爲j=i=10。也就是說,首先您的for循環完全運行,然後您的匿名函數將運行並使用已設置爲10的值j

在你的第一個例子中,情況有些不同。通過將呼叫打包到process.nextTick(...)自己的匿名函數中,並通過調用包裝函數將i的值綁定到函數本地範圍中(並且順帶將遮蔽舊變量i納入函數參數i),則捕獲該值的變量i在那時刻,而不是保留封閉參考i,其值在內部匿名函數的封閉中變化。

要澄清你的第一個例子,嘗試改變匿名包裝函數以使用名爲x(function (x) { process.nextTick(...); })(i))的參數。這裏我們清楚地看到x在調用匿名函數的時候取值爲i,所以它會得到for循環中的每個值(1..10)。

+0

什麼是捕獲值的最佳實踐解決方案,而不是保留封閉的參考? – 2017-10-23 21:24:50