2011-03-31 78 views
2

我正在使用JQuery和jsTree,並且我遇到了關於閉包如何工作的一些混淆。關閉Javascript關閉的問題

我有一個具有.jsTree成員和.populateTree方法的對象。該方法用一個字符串數組來調用,它應該創建jsTree的節點。

jsTree構建一個樹形控件,其中每個節點都有一個錨點「」,其中包含節點的文本。我想單擊文本切換節點打開或關閉,就像單擊樹中的+/-按鈕一樣。所以我試圖添加一個click()函數來做到這一點,並且我得到了意想不到的行爲。

所以,這裏是代碼:

populateTree: function populateTree(nodeNames) 
{ 
    if (!this.jsTree) // Note 1 
     return; 

    var me = this; // Note 2 

    for (var i = 0; i < nodeNames.length; i++) 
    { 
     var nodeName = nodeNames[i]; 

     var node = this.jsTree.create_node(-1, "last", { state: 'open', data: nodeName }); //Note 3 

     this.jsTree.create_node(node, "last", { data: "child one" }); // Note 4 
     this.jsTree.create_node(node, "last", { data: "child two" }); 
     this.jsTree.create_node(node, "last", { data: "child three" }); 

     var anchor = node.find("a"); // Note 5 
     anchor.click(function() { me.jsTree.toggle_node(node); }); // Note 6 
    } 
}, 
  • 注1:這是一個JavaScript對象的成員函數,所以當它被稱爲「這種」指向的對象。該對象包含一個jsTree成員變量,該變量應該已經被初始化爲包含一個jsTree對象,沒有節點。

  • 注2:我們是在注6定義一個「點擊」功能,而被調用時,「這」不會指向包含jsTree的對象,所以我們保存在「本」名爲「me」的變量,它將在執行「click」函數時處於範圍內,因爲創建該函數會創建一個閉包,該閉包包含對函數定義時範圍內的所有變量的引用。

  • 注3:對於數組中的每個元素,我們創建一個頂級節點(父節點爲-1)。

  • 注4:對於我們創建的每個頂級節點,我們創建三個子節點。

  • 注意5:每個節點都包含一個錨元素(「」),這就是我們想要附加一個「點擊」功能。

  • 注意6:在「click」函數中,「me」應指向包含樹的對象(請參見注釋2),並且「節點」應該指向剛剛創建的節點,電流通過迴路(見注3)。

我的問題?無論我點擊哪個錨點,它始終是打開和關閉的最後一個頂級節點。就像我們創建的每個「click」函數的閉包都有一個只引用最後一個「節點」變量的閉包。這不是我認爲閉包工作的方式。

有人能幫助我理解我的理解出錯的地方嗎?

謝謝。

+1

另外一個澄清一點:JavaScript語句塊(你的「for」循環的花括號塊)做**不**創建自己的詞彙範圍。它與C++或Java等語言不同。只有函數創建新的範圍。 – Pointy 2011-03-31 16:06:43

+0

如[這個問題]提到你有完全相同的問題(http://stackoverflow.com/questions/341723/event-handlers-inside-a-javascript-loop-need-a-closure)。請參閱已接受的答案解決方案 – schellmax 2011-03-31 16:04:06

回答

3

您連接到與單擊處理匿名函數關閉在node例如,一旦循環執行完畢,許多秒鐘後,當用戶點擊時執行匿名函數的樹,它就會查看它創建時關閉的範圍,並且看到node的值是它最後一次持有的值,就像您注意到的那樣,這是循環的最後一次迭代的值。

速戰速決可能是:

anchor.click((function(node){ return function() { me.jsTree.toggle_node(node); }; })(node)); 

這種方式,節點的關閉了值傳遞的值,這將保持每次迭代不同的值。

1

記住關閉是靜態的。在您的點擊處理程序中使用的節點的值將是它分配的最後一個值,而不是它在創建點擊處理程序時的值。

修復的方法是創建一個新的閉包爲每次點擊的處理程序:

var anchor = node.find("a"); // Note 5 
(function (node) { 
    anchor.click(function() { me.jsTree.toggle_node(node); }); // Note 6 
}) (node); 

匿名函數被調用節點的當前值,這是一個被調用的點擊處理程序時參考。每一次點擊處理程序提供與自己的節點

+0

我想你正在關注我遇到的問題。這並不是說我不懂閉包,而是我沒有正確理解變量。在一種理智的語言中,會有三個不同的「節點」實例,每次循環執行一次,就像有三種不同的「點擊」功能一樣。但是,顯然,沒有。 – 2011-03-31 16:05:53

2

價值的問題是,在JavaScript中,塊沒有定義範圍,功能做。

因此,即使nodeNamenode是在for循環內定義的,它們的作用相同,如果他們被定義之外,因爲for循環塊不會創建一個新的範圍。

這就是爲什麼在書中「的Javascript:好的部分」克勞福德建議在Javascript中你在一開始的功能,而不是最接近它定義局部變量的使用就像使用其他語言。進一步定義它們使得它看起來好像是在包含區塊內的範圍,而實際上它們不是。