“this”并未引用使用原型的this-reference

时间:2012-10-03 06:05:44

标签: javascript javascript-events prototype

今天我使用Javascript的prototypethis引用遇到了一个非常奇怪的问题。

基本上,我有两个对象。一个对象使用div元素,并且可以在onclick上注册div事件。另一个对象使用此功能并使用第一个对象注册onclick事件。

以下是代码:

我的第一个对象,负责div

DivObject = function() {};
DivObject.prototype.setClickListener = function(clickListener){
    document.getElementById("myDiv").onclick = function(e){
        clickListener(e);
    };
};

我的第二个对象使用此功能:

MainObject = function(){
    this.myString = "Teststring";
    this.divObj = new DivObject();
    this.divObj.setClickListener(this.handleClick);
};

MainObject.prototype.handleClick = function(e){
    // do something with e
};

问题是,在MainObject.prototype.handleClick内,this引用引用window对象,而不引用MainObject。因此,此函数中的console.log(this)会记录[Object window]

You can see this behaviour on jsfiddle.

作为一种解决方法,我使用setClickListener函数,如下所示:

var thisRef = this;
this.divObj.setClickListener(function(e){
    thisRef.handleClick(e);
});

现在,this函数中的handleClick引用引用了我的MainObject(我希望它是这样)。

See this workaround on jsfiddle.

我的问题是:

为什么this引用一次引用window对象,一次引用我对象的this引用?它在某处被覆盖了吗?我一直认为在我的对象中使用this我可以肯定它实际上是我对象的this引用?我现在使用的解决方法是如何解决这个问题的方法,还是有其他更好的方法来处理这种情况?

2 个答案:

答案 0 :(得分:1)

您可以使用 .bind() 解决此问题:

this.divObj.setClickListener(this.handleClick.bind(this));

See the demo.

答案 1 :(得分:1)

您的问题:

  

为什么这个引用一次引用窗口对象,一次引用我对象的this引用?

除了使用 bind 之外,函数this的值由函数的调用方式设置。当你这样做时:

this.divObj.setClickListener(this.handleClick);

您正在分配函数引用,因此无需任何限定即可调用该函数。在进入函数时,由于调用未设置其this,因此它将默认为全局(窗口)对象,或者在严格模式下保持未定义。

  

它在某处被覆盖了吗?

不,在进入执行上下文时设置this的值。您无法覆盖它或直接分配给它,您只能在调用中设置它(例如,作为对象的方法,使用 new apply 调用)或使用 bind

  

我一直认为在我的对象中使用它我可以肯定它实际上是我对象的这个引用?

在您完成作业时,this就是您所期望的。但是你要分配一个对funciotn的引用,而不是调用该函数,所以它的this不会在那个时刻设置,而是在它被调用的时候设置。

  

我现在使用的解决方法是如何解决这个问题的方法,还是有其他更好的方法来处理这种情况?

你的工作很好(和一个常见的修复),它创建一个闭包,所以可能会有轻微的记忆后果,但没有什么严重的。对于非常旧版本的IE,由于涉及DOM对象的循环引用会导致内存泄漏,但这是固定的。

从清晰度和维护角度来看, bind 解决方案可能更好。请记住为没有内置支持 bind 的浏览器添加“猴子补丁”。

请在SO上发布代码,不保证在其他地方发布的代码将继续可访问。围绕代码的工作:

MainObject = function(){
    this.myString = "Teststring";
    this.divObj = new DivObject();
    var thisRef = this;
    this.divObj.setClickListener(function(e){
        thisRef.handleClick(e);
    });
};