1

prototype今天我在使用 Javascript和this参考时遇到了一个非常奇怪的问题。

基本上,我有两个对象。一个对象使用了一个div元素,并且可以onclickdiv. 另一个对象利用此功能并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.handleClickthis引用指的是window对象,而不是MainObject. 所以,console.log(this)在这个函数里面 logs [Object window]

您可以在 jsfiddle 上看到这种行为。

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

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

现在,this我的handleClick函数中的引用指的是我的MainObject(如我所愿)。

请参阅 jsfiddle 上的此解决方法。

我的问题是:

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

4

2 回答 2

1

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

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

请参阅演示。

于 2012-10-03T06:12:38.613 回答
1

你的问题:

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

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

this.divObj.setClickListener(this.handleClick);

您正在分配一个函数引用,因此该函数稍后会在没有任何限制的情况下被调用(即它被称为handleClick而不是this.handleClick)。在进入函数时,因为它this不是由调用设置的,所以它将默认为全局(窗口)对象,或者在严格模式下保持未定义。

它是否在某处被覆盖?

不, 的值this是在进入执行上下文时设置的。您不能覆盖它或直接分配给它,您只能在调用中设置它(例如,作为对象的方法,使用newapplycall)或使用bind(此外,箭头函数采用其封闭的this词法执行上下文)。

我一直认为在我的对象中使用 this 可以确定它真的是我的对象的 this 引用?

在您进行分配时,this这就是您所期望的。但是您正在分配对funciotn的引用,而不是调用该函数,因此this当时没有设置它,而是稍后调用它。

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

您的解决方法很好(并且是一个常见的修复方法),它会创建一个闭包,因此可能会产生轻微的内存后果,但没有什么严重的。对于非常旧的 IE 版本,由于涉及 DOM 对象的循环引用,它会造成内存泄漏,但这是固定的。

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

请在 SO 上发布代码,不能保证其他地方发布的代码将继续可访问。解决代码:

MainObject = function(){
    this.myString = "Teststring";
    this.divObj = new DivObject();
    var thisRef = this;
    this.divObj.setClickListener(function(e){
        thisRef.handleClick(e);
    });
};
于 2012-10-03T06:41:38.457 回答