2013-10-27 103 views
1

我有一個問題,我無法解決。如何避免「this」引用DOM元素,並引用對象

上下文是:我想要一個繼承鏈,並且屬於這個繼承的對象的方法必須是事件處理程序,並且同時能夠到達對象屬性。

我試圖編寫沒有「新」字的JavaScript,而是使用Object.create()和一些繼承層次結構。所以首先這種方法。

所以,我有我的其餘對象(myProto)的藍圖,然後我創建的Object.create對象(所以沒有封在那裏我可以做到分配thisthatself的伎倆)。現在,當我使用此對象的方法處理例如div上的單擊事件時,this顯然會引用DOM對象,並且我失去了訪問我的對象屬性的可能性。

var myProto = { 
    init: function (name, value) { 
     this.name = name; 
     this.value = value; 

     return this; 
    }, 
    someHandler: function (e) { 
     // Normally I would use this instead of e.target... 

     e.target.innerHTML = this.name + this.value; // This does not refer to the object. 
    } 
}; 

var myObject = Object.create(myProto).init('myName', 'myValue'); 
document.getElementById('myDiv').onclick = myObject.someHandler; 

這裏小提琴:http://jsfiddle.net/pgPHM/

而現在的「經典」的方法:如果我用的是新的構造形式,它會很容易在封閉轉讓本,然後訪問它,但有是在Constructor.prototype

var Constructor = function (name, value) { 
    var self = this; 

    self.name = name; 
    self.value = value; 
}; 

Constructor.prototype.someHandler = function() {/*self does not reach this here*/}; 

的jsfiddle功能的問題:http://jsfiddle.net/ZcG3J/2/

我真的不知道爲什麼JS對象沒有一個真正的thisself或任何稱自己沒有這些棘手的背景下,關閉等...

基本問題是:

如何使用的方法一個對象作爲事件處理程序,並且仍然能夠到達該對象?

回答

2

除了使用new時,Javascript中的this根據函數的調用方式來設置。把它叫做正確的方式,this將是你想要的。我無法確定你想要解決什麼問題,但以下是如何確定this

  1. 調用obj.method() - this會的method()內部設置爲obj
  2. 使用function.call()function.apply()來控制什麼this將是你自己。
  3. 進行正常的函數調用,如func()this將被設置爲全局對象或undefined,具體取決於您是否處於嚴格模式。
  4. 使用.bind()(在現代瀏覽器中)創建一個函數存根,內部自動使用.apply()將其值綁定到該函數的執行。
  5. 當您使用new調用構造函數時,this將被設置爲構造函數內新創建的對象。
  6. 當您將回調函數作爲參數傳遞給任何函數調用時(例如addEventListener(),調用函數的責任是決定要將this指針設置爲並且不會綁定到您自己的對象。 addEventListener()this是導致該事件的DOM對象。

事件處理程序,你想方法調用特定對象上,你必須創造一種與對象的方法調用相關聯。您可以使用上面指定的.bind()或使用匿名函數,該函數可以在封閉中引用保存的值this

var self = this; 
document.addEventListener('keyup', function(e) { 
    self.handleKeys(e); 
}) 

,或者使用.bind()

document.addEventListener('keyup', this.handleKeys.bind(this)); 

僅供參考,那裏是因爲.bind()只是創建了一個封閉和做的第一個例子做什麼,但它會爲你不是這兩種方法之間的任何實際功能差異。

+0

如何使用對象的方法作爲事件處理函數,並仍然能夠到達對象? – bgusach

+0

@ ikaros45 - 請參閱我的答案中的選項4。 – jfriend00

+0

@ ikaros45 - 添加了幾個例子。 – jfriend00

2

簡單。如果一個函數被稱爲一種方法而不是「裸」,那麼this總是指向最後一個點之前的單詞。因此,而不是:

document.getElementById('myDiv').onclick = myObject.someHandler; 

// You're just passing the function here, not calling it. 
// It will be called by the onclick handler so `this` is changed 

做到這一點:

document.getElementById('myDiv').onclick = function(){ 
    myObject.someHandler(); 

    // Here, you're actually calling it. So this is the word 
    // before the last dot. Which is myObject 
} 

在更現代的JavaScript,你當然可以用bind

document.getElementById('myDiv').onclick = myObject.someHandler.bind(myObject); 
+0

有關「this」工作原理的詳細說明,請參閱:http://stackoverflow.com/questions/13441307/how-does-the-this-keyword-in-javascript-act-within-an-object-literal/13441628 #13441628 – slebetman

1

在你的jsfiddle的第一個問題是, self是構造函數的局部變量,它不在函數之外。你想想下面的代碼是什麼:

var Constructor = function(name, value) { 
    var self = this; 
    self.name = name; 
    self.value = value; 
    self.someHandler = function(e) { 
     e.target.innerHTML = self.name + self.value; // self undefined  
    } 
    return self; 
}; 

var myObject = Constructor('myName', 'myValue'); 
document.getElementById('myDiv').onclick = myObject.someHandler; 

的jsfiddle - >http://jsfiddle.net/ZcG3J/4/

是隻要你想它的結構?

+0

這很容易,而且工作,但不是使用構造函數的.prototype對象。如果你想多次實例化構造函數,它不是那麼高效。基本上你是從零開始構建一個對象,而不是使用原型繼承。 – bgusach

+0

嗯......就我所知,如果你使用* new *關鍵字,你也在創建新的對象。原型繼承在某些情況下可能會非常混亂。這就是爲什麼我更喜歡揭示模塊模式。 – Krasimir

+0

你如何將揭示模式與繼承結合起來?看起來很難讓我使用該模式創建的另一個對象繼承對象,或者Crockford的「SuperConstructor」類似。 – bgusach