处理包含由事件监听器调用的“this”关键字的Object方法中的Scope

时间:2011-12-01 05:06:06

标签: javascript oop events scope

我把这种情况的缺乏理解归结为这个小问题。到目前为止,我认为这就是我所知道的:

我有一个对象myDog(一个全局变量)。 Dog有一个成员变量el,它是一个html元素;因为它是一个元素,我可以添加事件监听器。因此,当您点击myDog.el时,它会在控制台中记录this.namemyDog.name的值。正如预期的那样,范围this.name未定义且myDog.name为'tye'。当由click事件侦听器调用时,this内的Dog.speak引用被单击的元素,即成员变量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元素的策略是什么,以便我可以从this.el返回myDogthis.el的所有者)而不myDog是一个全球变量?

2)在这种情况下全局变量是否是必要的邪恶?如果是这样,在这种情况下优雅使用它们的优秀策略是什么?例如,如果我想要100只狗实例化怎么办?我如何处理Dog.speak中的所有全局变量?

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

3 个答案:

答案 0 :(得分:5)

  

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

由于您正在使用.addEventListener(),我建议您充分利用其中很少有人知道的功能......让您的Dog对象实现{ {1}}界面。

这在您的EventListener数据及其相关元素之间建立了非常干净的关系。

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


DEMO: http://jsfiddle.net/Ewgw5/1/

Dog

所以我所做的就是将构造函数中的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获得。


  

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

谢天谢地!


此行为应该是event.currentTarget的垫片的一部分。

答案 1 :(得分: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()中添加的匿名函数即使在包含函数返回后也可以访问包含函数的范围,因此它可以使用selfthis被称为构造函数时,它保存对从Dog()保存的原始对象的引用。

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

答案 2 :(得分:1)

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

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

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