2011-11-21 15 views
6

我是C#開發人員,習慣於在C#中使用閉包方式工作。 目前,我有匿名的JavaScript函數的工作,並與下面的代碼段遇到了一個問題:以匿名方式訪問複製的整型變量

function ClosureTest() { 
    var funcArray = new Array(); 

    var i = 0; 
    while (i < 2) { 
     var contextCopy = i; 

     funcArray[i] = function() { alert(contextCopy); return false; }; 

     i++; 
    } 

    funcArray[0](); 
    funcArray[1](); 
} 

我預計第一funcArray()電話說0和第二說1。但是,他們都說1。這怎麼可能?

通過書寫var contextCopy = i我確保我創建了一個i-變量的副本。然後,在每次迭代中,我創建一個全新的函數指針。每個功能都指自己的i副本,即contextCopy。但是,由於某種原因,兩個創建的函數都指向相同的變量contextCopy

這是如何工作在JavaScript?

+0

有沒有原因你不使用'for'循環? – zzzzBov

+1

不存在;-) – TwinHabit

回答

11

JavaScript有詞法關閉,而不是塊關閉。即使你正在將我分配給contextCopy,contextCopy本身也是ClosureTest的一個詞彙成員(與C#不同,{}爲您提供了一個新的作用域塊)。試試這個:

while (i < 2) { 
    funcArray[i] = (function(value) { 
     return function(){ alert(value); return false; } 
    })(i); 
    i++; 
} 
+0

謝謝您的這個工作示例。在這裏介紹了這些解釋之後,我很清楚,爲什麼我最初的嘗試不起作用。 – TwinHabit

7

JavaScript中的大括號({})不像在C#中那樣捕獲變量。

只有閉包(函數)引入了新的範圍和捕獲變量。

var i = 0; 
while (i < 2) { 
    var contextCopy = i; 
    ... 
} 

實際上被解釋爲:

var i, contextCopy; 
i = 0; 
while (i < 2) { 
    contextCopy = i; 
    ... 
} 

爲了得到變量的副本,你需要一個封閉包裝代碼:

var i; 
i = 0; 
while (i < 2) { 
    (function (contextCopy) { 
    ... 
    }(i)); 
} 
+1

+1用於指出contextCopy變量的提升(並不像我的回答那樣清晰) – Matt

+0

非常感謝您的澄清 – TwinHabit

0

你不創建一個i變量的副本。相反,您可以根據使用它的閉包使這個變量依賴於GC。這意味着當while循環退出時,變量i繼續處於其最後狀態(1),並且兩個閉包都參考它。

另一種方式來說明:關閉一個變量不會將它複製到閉包中(對於對象來說沒什麼意義),它只是讓閉包引用變量,並確保該變量在閉包之前不會被GCed。