3

我目前正在为我正在开发的游戏编写一个类,该类根据页面 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”上下文会发生变化吗?

4

3 回答 3

4

由于您使用的是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以查看它是哪个事件。

in的thishandleEvent将是对象,它使您可以直接访问其方法。


如果您使用构造函数,此技术也很有用,因为您可以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
        }
    }
}();
于 2012-11-23T15:48:04.367 回答
4

我希望我提供的链接可以清楚地说明您的问题的原因。

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

 window.addEventListener('hashchange', this.route.bind(this), false);
于 2012-11-23T15:49:27.987 回答
2

您需要使用闭包:

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

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

    }
})(this),
于 2012-11-23T15:43:23.583 回答