【问题标题】:Context of this changing when using window.addEventListener inside class在类中使用 window.addEventListener 时更改的上下文
【发布时间】:2012-11-24 00:13:46
【问题描述】:

我目前正在为我正在开发的游戏编写一个类,该类根据页面 URL 中的哈希更改来控制应用程序路由。

我的问题是在将主路由函数附加到 hashchange 事件后,“this”的上下文变为“window”。

这是目前为止的代码:

Game.Router = function() {

return {

    init: function() {

        window.addEventListener('hashchange', this.route, false);

    },

    route: function(e) {

        e.preventDefault();
        var routingLocation = e.newURL.substr(e.newURL.indexOf('#!/') + 3, e.newURL.length);

        switch(routingLocation) {
            case "new":
                this.toggleView('Game');
                break;
            case "instructions":
                this.toggleView('Instructions');
                break;
            case "scores":
                this.toggleView('Scores');
                break;
            case "about":
                this.toggleView('About');
                break;
        }

    },

    toggleView: function(viewID) {

        var els = document.querySelectorAll('section');
        for(var i=0, l=els.length; i<l; i++) {
            if(els[i].id == viewID) {
                els[i].className = 'currentGameSection';
            } else {
                els[i].className = '';
            }
        }

    }

}

}();

当我尝试在路由函数的 switch 语句中调用 this.toggleView 时,结果发现“this”已从 Game.Router 更改为 window。问题可以通过将 this.toggleView 替换为 Game.Router.toggleView 来解决,但这并不理想。

有人可以帮我理解为什么添加事件侦听器后“this”上下文会发生变化吗?

【问题讨论】:

  • 这是关于 SO 最重复的问题之一。我建议您阅读以下内容,而不是寻找完全相同的副本:stackoverflow.com/questions/1085674/…
  • 是的,我有一种感觉。搜索结果没有任何用处......

标签: javascript


【解决方案1】:

我希望the link I gave 说明您的问题原因。

为了不给出通常的闭包解决方案,这里提供一个更简单的(不兼容IE8-):

 window.addEventListener('hashchange', this.route.bind(this), false);

【讨论】:

  • addEventListener 也不兼容 IE8,所以这真的不是问题。 :-) 只有我知道支持aEL 但不支持.bind 的浏览器是 Safari 5.1.4 及更低版本。
  • 谢谢,这是最适合我的解决方案 :)
  • @user1689607 我没有想到这一点。谢谢(和 +1)
【解决方案2】:

由于您使用的是addEventListener,只需向对象添加handleEvent 方法,并传递对象而不是处理程序。

Game.Router = function() {

    return {
          // vv--- add this
        handleEvent: function(e) {
            if (e.type === "hashchange")
                return this.route();
        },

        init: function() {
                             // pass the object---vv
            window.addEventListener('hashchange', this, false);
        },

        route: function(e) {
            // your route code
        },

        toggleView: function(viewID) {
            // your toggleView code
        }
    }
}();

这样做是为了让对象实现 EventListener 接口。因此,对象本身成为有效的事件处理程序,并且可以传递给addEventListener 而不是函数。

所以当任何偶数发生时,handleEvent 方法将被调用。您可以测试.type 以查看它是哪个事件。

handleEvent 中的 this 值将成为对象,让您可以直接访问其方法。


如果您使用构造函数,此技术也很有用,因为您可以使用其他方法对handleEvent 进行原型设计,并且所有继承该原型的对象都将实现 EventListener 接口.


如果您要使用闭包,我会将封闭变量放在 Game.Router 函数中,以便任何和所有方法都可以利用它。

Game.Router = function() {
    var self = this;
    return self = {
        init: function() { // using 'self' in case 'init' becomes detached
            window.addEventListener('hashchange', self.route, false);
        },

        route: function(e) {
            // use 'self' to refer to the object
        },

        toggleView: function(viewID) {
            // use 'self' to refer to the object
        }
    }
}();

【讨论】:

  • 感谢您的解释...我不知道handleEvent。应该对未来有用!
  • @thesonglessbird:不客气。这不是addEventListener 的一个众所周知的功能,但非常有用,尤其是在您使用构造函数创建对象的许多实例时。
【解决方案3】:

你需要使用闭包:

route: (function(context){
    return function(e){

        //your original function, but with all
        //references to this changed to
        //context

    }
})(this),

【讨论】:

  • 如果为此使用闭包,我认为您不妨先将对象分配给变量,从而利用Game.Router 创建的现有闭包在返回之前。这将处理所有方法的任何this 问题。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2022-01-20
  • 1970-01-01
  • 1970-01-01
  • 2018-12-02
  • 2013-12-05
  • 2012-02-02
  • 1970-01-01
相关资源
最近更新 更多