2011-07-18 65 views
2

我有一個定義了「類」的類,並且只創建了它的一個實例。該實例擁有一個最終會被傳遞的成員函數(這是一個鼠標處理程序,但這不重要)。由於我只會創建我的「類」的一個實例,因此我決定使用對象字面值將其重寫爲單例。當傳遞一個對象的成員函數時綁定「this」

所以我

var mySingleton = { 
    theObjects : []; 
} 

mySingleton.mouseHandler = (function() { 
    var that = this; 
    return function (e) { 
     for (var indx = 0; indx < that.theObjects.length; indx++) { 
      // do something to that.theObjects[indx]; 
     } 
    } 
}()); 

mySingleton.addObject = function(newObj) { 
    this.theObjects.push(newObj); 
} 

然而,當我嘗試使用此代碼(增加了一些對象後),我不斷收到that.theObjects是未定義的錯誤。它是指for循環中的行。

+0

line'var mySingleton = {theObjects = []; }'應該是'var mySingleton = {theObjects:[]}'。不知道這個問題雖然 –

+0

你只是寫了兩次相同的事情... – fletom

+0

哦,對不起,我在我的計算器問題中犯了一個錯字。這在我的代碼中確實是冒號,所以這不是問題。我會相應地編輯我的帖子。 – achow

回答

7

更新爲2015年 –使用Function.bind()到函數中指定的this值。然後,而不是使用that,您可以使用this

mySingleton.mouseHandler = function (e) { 
    for (var indx = 0; indx < this.theObjects.length; indx++) { 
     // do something to this.theObjects[indx]; 
    } 
}.bind(mySingleton); 

,如果你想mouseHandler有「打探」元素的情況下這是行不通的。爲此,請使用我原來的答案。

如果您之前需要支持IE8或(天堂禁令),則需要使用polyfill


既然你撥打立即創建mouseHandler的功能,它的window,不mySingleton上下文中運行。所以that是指window。而不是立即調用它,只需將其更改爲一個方法,以便它運行在mySingleton背景:

mySingleton.getMouseHandler = function() { 
    var that = this; 
    return function() { ... }; 
}; 
myElement.onclick = mySingleton.getMouseHandler(); 

當然,既然你已經使用了單,你可以直接引用。在您的點擊處理程序中,不要檢查that.theObjects,請檢查mySingleton.theObjects。或者,在mouseHandler更改var that = thisvar that = mySingleton

編輯:或者,通過上下文的匿名函數,當你把它叫做:

mySingleton.mouseHandler = (function() { 
    var that = this; 
    return function (e) { 
     for (var indx = 0; indx < that.theObjects.length; indx++) { 
      // do something to that.theObjects[indx]; 
     } 
    } 
}).call(mySingleton); 
+0

但是,然後getMouseHandler現在是一個生產函數對象的工廠... – achow

+0

@highwind - 確實。點擊處理程序是函數對象。另一種方法是在閉包中創建整個對象。 – gilly3

+0

是的,但是對於處理程序只有一個函數對象,而不是生成它們的工廠會更高效嗎? – achow

1

有幾種流行的方法來做到這一點。首先,超簡單的解決方案是直接引用mySingleton,並繞過與this相關的混淆。而不是that.theObjects只是做mySingleton.theObjects並繼續你的生活,事情會正常工作。

但是,有一個共同的模式來做這個綁定。下面是how underscore.js does it

退房的annoted source to underscore,在那裏你會發現這個

_.bind = function(func, obj) { 
    if (func.bind === nativeBind && nativeBind) return nativeBind.apply(func, slice.call(arguments, 1)); 
    var args = slice.call(arguments, 2); 
    return function() { 
     return func.apply(obj, args.concat(slice.call(arguments))); 
    }; 
    }; 
+0

(對不起,不知道如何在註釋中放置代碼塊...) 如果mySingleton位於全局名稱空間中,則「mySingleton.theObjects」將起作用。所以它不會推廣到例如在函數中聲明的「var mySingleton」。如何獲得一個通用的解決方案? 至於「做這種綁定的一種常見模式」,我的印象是創建一個閉包並將「this」綁定到「this」是做事的常用方式(按照Crockford)。爲什麼我的代碼不工作。我拉我的頭髮,因爲我簡直不明白爲什麼它不起作用... – achow

+0

@highwind - 你的代碼不起作用,因爲創建'mouseHandler'的匿名函數在全局上下文中運行,所以''那個'最後提到'this'。看到我的答案。 – gilly3

+0

@ gilly3 - 這很奇怪。我想我會遇到這樣一個愚蠢的錯誤。哦,我看到你做出了答案。 – achow

0

這裏其他的答案至今都還正確。提供我的觀點以防萬一。

理解爲什麼代碼不像您期望的那樣行事的關鍵需要了解this如何在JavaScript中工作。問題是this取決於如何調用該函數。

首先,如果你調用函數的方法風格,this是你所期望:

mySingleton.mouseHandler(); // this === mySingleton 

如果附加功能的東西esle,那也可以。

var anotherSingleton = {}; 
anotherSingleton.foo = mySingleton.mouseHandler; 
anotherSingleton.foo(); // this === anotherSingleton 

如果分離的功能,this成爲全球範圍內的對象(window

var foo = mySingleton.mouseHandler; 
foo(); // this === window 

最後,你可以強制this別的東西使用callapply

var randomThingy = {}; 
mySingleton.mouseHandler.call(randomThingy); // this === randomThingy 

外賣是this確定在runti我基於如何調用函數的上下文。通常,允許您創建「類」的框架通過以您的名義隱式應用綁定模式來從您那裏抽象出這些細節。這就是爲什麼它曾經工作,而不再是如此。

正如其他人所提到的,您可以更改您的處理程序,以便通過其作用域名稱(mySingleton)引用該變量,或者按照討論的方式將其綁定。

這裏有一篇文章幾年前我關於這個問題寫道,進入更多細節:http://trephine.org/t/index.php?title=Understanding_JavaScript%27s_this_keyword

希望這有助於!

+1

非常感謝 – 2013-07-23 08:31:30

相關問題