【问题标题】:Dealing with Scope in Object methods containing 'this' keyword called by Event Listeners处理由事件侦听器调用的包含“this”关键字的对象方法中的范围
【发布时间】:2012-01-10 07:53:01
【问题描述】:

我已经将我对这种情况的缺乏理解概括为这个小问题。以下是我认为到目前为止我所知道的:

我有一个对象myDog(一个全局变量)。 Dog 有一个成员变量 el 是一个 html 元素;因为它是一个元素,所以我可以向它添加事件侦听器。因此,当您单击myDog.el 时,它会将this.namemyDog.name 的值记录到控制台。正如预期的那样,this.name 是未定义的,myDog.name 是“tye”。 Dog.speak 内部的this 在被点击事件监听器调用时指的是被点击的元素,成员变量el,而不是对象Dog。由于myDog 是一个全局变量,因此无论函数的作用域如何,它都可以恢复并返回myDog.name

见下面的代码:

function Dog(name,id) {
    this.name = name ? name : "spot";
    this.id = id ? id : "dog";
    this.el = document.getElementById(this.id); // given there is a div with a matching    
    this.el.addEventListener("click",this.speak); // ignore IE for simplicity (attachEvent has its own 'this' scope issues)
}

Dog.prototype = {
    speak: function() {
        console.log("this.name: "+this.name+"\nmyDog.name: "+myDog.name);
    }
};

var myDog = new Dog("tye","dog1");

所以...我的问题是

1) 将对象附加到 html 元素的一些策略是什么,以便我可以在 myDog 不作为全局变量的情况下从 this.el 回到 myDogthis.el 的所有者)?

2) 在这种情况下,全局变量是必要的吗?如果是这样,在这种情况下有什么好的策略可以优雅地使用它们?例如,如果我想要实例化 100 条狗怎么办?我将如何处理Dog.speak 中的所有这些全局变量?

如果你想玩,这里有一个 jsfiddle 版本:http://jsfiddle.net/chadhutchins/Ewgw5/

【问题讨论】:

  • 解决 this 范围问题的最常见方法之一是在函数开头将“this”分配给变量...self 将始终引用原始 this。
  • 感谢您的回复,但您能进一步解释一下吗?如何在上面的示例中使用该技术。
  • 我也照Bones牧师说的做

标签: javascript oop events scope


【解决方案1】:

"1) 将对象附加到 html 元素的一些策略是什么..."

由于您使用的是.addEventListener(),我建议您利用它的一个似乎很少有人知道的功能......让您的Dog 对象实现EventListener 接口。

这在您的Dog 数据及其关联元素之间建立了非常清晰的关系。

只需要进行微小的更改。代码优先...解释如下。


演示: http://jsfiddle.net/Ewgw5/1/

function Dog(name,id) {
    this.name = name ? name : "spot";
    this.id = id ? id : "dog";
    this.el = document.getElementById(this.id);

    // ---------------------------------v----no function!
    this.el.addEventListener("click", this);
}

Dog.prototype = {
    // Implement the `EventListener` interface   
    handleEvent: function(event) {
        switch (event.type) {
            case "click": return this.speak();
        }
    },
    speak: function() {
        console.log("this.name: "+this.name+"\nmyDog.name: "+myDog.name);
    }
};

var myDog = new Dog("tye","dog1");

所以我所做的只是将构造函数中的this 传递给addEventListener(),而不是传递函数,然后我将handleEvent() 方法添加到Dog.prototype

现在当"click" 事件发生时,它将调用handleEvent() 方法。该方法中this 的值将是您的Dog 实例。所以你可以从那里调用任何你需要的方法。

因为您将元素设为this 的属性,所以您可以通过this.el 访问该元素。但这在技术上甚至没有必要,因为该元素也可以通过event 对象作为event.currentTarget 使用。


"2) 在这种情况下,全局变量是否是必要的邪恶..."

谢天谢地没有!


这种行为应该是.addEventListener() 的填充程序的一部分。

【讨论】:

  • 面向对象事件处理的好方法。如果您有多个实例监听同一个事件,但您根据实例的本地字段以不同方式处理它。
【解决方案2】:

https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Function/bind

 this.el.addEventListener("click", this.speak.bind(this));

这种方式更可取,因为它不需要分配范围来传递绑定。范围分配是 JS 中成本较高的事情之一。

【讨论】:

  • 这是迄今为止提供的答案中最干净的解决方案,也是我所知道的最佳解决方案。
【解决方案3】:

你可以试试这样的:

function Dog(name,id) {
   var self = this;   // <---- NEW BIT saving a reference to this
   this.name = name || "spot"; // <-- unrelated tidy-up to use || instead of ?:
   this.id = id || "dog";
   this.el = document.getElementById(this.id); // given there is a div with a matching    
   this.el.addEventListener("click",function(){ self.speak(); });
        // ignore IE for simplicity (attachEvent has its own 'this' scope issues)
}

通过闭包的魔力,我在addEventListener() 中添加的匿名函数即使在包含函数返回后也可以访问包含函数的范围,因此它能够使用self,其中包含对当 Dog() 被调用为构造函数时,从 this 保存的原始对象。

编辑:抱歉,我没有直接解决您编号为 (1) 和 (2) 的问题,但正如您所见,您不需要全局变量来解决此问题。使用我描述的技术,您可以实例化 100 条狗,它们都可以工作。 (好吧,无论如何他们都会说话:如果你添加Dog.prototype.work = function() { },他们都会工作。)

【讨论】:

  • 感谢您在“闭包的魔力”之外解释它。这真的很有意义。另外,感谢有关成员变量清理的提示。
  • 只是为了澄清。调用函数时,函数中的“this”将绑定到拥有用于调用该函数的引用的对象。
  • 请记住,您可以使用.apply().call() 显式设置this
猜你喜欢
  • 2016-04-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-08-17
  • 2022-01-20
  • 1970-01-01
  • 1970-01-01
  • 2014-05-02
相关资源
最近更新 更多