Javascript “this” overridden by event listener [du

2019-06-28 07:50发布

问题:

This question already has an answer here:

  • Dealing with Scope in Object methods containing 'this' keyword called by Event Listeners 3 answers

I am probably doing something wrong but I found some interesting behavior when trying to apply some object oriented programming to Javascript. Consider the following

function Bug(element) {
    this.focusedCell = null;
    element.addEventListener('click', this.onClick, true);
};

Bug.prototype.onClick = function(event){
    console.log("this is: ");
    console.log(this);
};

When I call the method from the console, I see the correct instance of "this" but when I click the element in the document I see the document element in lieu of the instance. So... chances are it's probably not a good idea to use event listeners with instance methods, at least the way I'm doing it.

So the question is:

  • Is it possible to have an event listener like this that calls an instance method of a javascript object, while preserving the instance in the call?

  • Is there a better pattern for doing this?

Edit: I haven't tried this in anything but Chrome. But I would imagine that the behavior is the same.

回答1:

There's a better pattern, and doesn't require much change. I'll show the code first.

function Bug(element) {
    this.focusedCell = null;
    // --------------------------------v----pass the object, not a function
    element.addEventListener('click', this, true);
};

// Implement the `EventListener` interface
Bug.prototype.handleEvent = function(event) {
    if (event.type === "click")
        this.onClick(event);
}

Bug.prototype.onClick = function(event) {
    console.log(JSON.stringify(this));         // '{"focusedCell":null}'
    console.log(event.currentTarget.nodeName); // "DIV"
};

By adding the handleEvent method, we make Bug implement the EventListener interface. This allows us to pass the new Bug object as the second argument to addEventListener() instead of a function.

Now when the "click" event happens, the .handleEvent() method will be invoked, and the value of this in that method will be the Bug object that was bound.


Since this is a reference to the Bug instance, it obviously won't be a reference to the element anymore. But it's not necessary, since the element is available via event.currentTarget.

Of course, you could add the element directly to your Bug object in the constructor if desired.

DEMO: http://jsfiddle.net/CnZTa/




回答2:

You can use Function.prototype.bind to create a listener bound to whatever this value you want:

function Bug(element) {
    this.focusedCell = null;
    element.addEventListener('click', this.onClick.bind(this), true);
};

Older (non-ES5) browsers will need a polyfill such as the one from MDN.



回答3:

This is normal behavior in JavaScript. You can preserve your expected this by passing an function the the listener:

function Bug(element) {
    var self = this; // Store a reference to this
    this.focusedCell = null;
    element.addEventListener('click', function() {
        // in here this refers to element
        self.onClick(event);
    }, true);
};

Bug.prototype.onClick = function(event){
    console.log("this is: "); 
    console.log(this); // In here this refers the an instance of Bug.
};