admin管理员组

文章数量:1356585

I'm currently writing a class for a game I'm working on that controls application routing based on hash changes in the page URL.

My problem is that the context of "this" changes to "window" after attaching the main routing function to the hashchange event.

Here is the code so far:

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 = '';
            }
        }

    }

}

}();

When I try and call this.toggleView in the route function's switch statement, it turns out that "this" has changed from Game.Router to window. The problem can be fixed by replacing this.toggleView with Game.Router.toggleView, but this isn't ideal.

Could someone please help me understand why the "this" context changes after adding the event listener?

I'm currently writing a class for a game I'm working on that controls application routing based on hash changes in the page URL.

My problem is that the context of "this" changes to "window" after attaching the main routing function to the hashchange event.

Here is the code so far:

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 = '';
            }
        }

    }

}

}();

When I try and call this.toggleView in the route function's switch statement, it turns out that "this" has changed from Game.Router to window. The problem can be fixed by replacing this.toggleView with Game.Router.toggleView, but this isn't ideal.

Could someone please help me understand why the "this" context changes after adding the event listener?

Share Improve this question edited Nov 23, 2012 at 15:42 DarkAjax 16.2k11 gold badges57 silver badges66 bronze badges asked Nov 23, 2012 at 15:40 thesonglessbirdthesonglessbird 5701 gold badge7 silver badges16 bronze badges 2
  • 2 This is one of the most duplicated question on SO. Rather than looking for an exact duplicate I can suggest to read this : stackoverflow./questions/1085674/… – Denys Séguret Commented Nov 23, 2012 at 15:44
  • Yeah, I had a feeling it might be. Searching turned up nothing useful though... – thesonglessbird Commented Nov 23, 2012 at 15:59
Add a ment  | 

3 Answers 3

Reset to default 4

I hope the link I gave makes the reason of your problem clear.

In order not to give the usual closure solution, here's a simpler one (not patible with IE8-) :

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

Since you're using addEventListener, just add a handleEvent method to the object, and pass the object instead of the handler.

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
        }
    }
}();

What this does is that it causes the object to implement the EventListener interface. Therefore the object itself bees a valid event handler, and can be passed to addEventListener instead of a function.

So when any even occurs, the handleEvent method will be invoked. You can test for the .type to see which event it was.

The this value in handleEvent will be the object, which gives you direct access to its methods.


This technique is also useful if you're using a constructor function, because you can just prototype the handleEvent with the other methods, and all objects that inherit that prototype will implement the EventListener interface.


If you were to use a closure, I'd put the closed-over variable in the Game.Router function so any and all methods can take advantage of it.

Game.Router = function() {
    var self = this;
    return self = {
        init: function() { // using 'self' in case 'init' bees 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
        }
    }
}();

You need to use a closure:

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

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

    }
})(this),

本文标签: javascriptContext of this changing when using windowaddEventListener inside classStack Overflow