"This" within es6 class method

30,251

Why is the "this" inside of of the Clickers' click method referring to the dom node rather than ... itself?

Because the specification for .addEventListener() is to set the this pointer to the DOM element that caught the event. That's how it is designed to work.


When passing a method as a callback where you want to override the value of this, you can use .bind() to force the desired value of this with it:

this.elem.addEventListener('click', this.click.bind(this));

Explanation:

All function calls in Javascript set a new value of this according to how the function is called. See this explanation for further info on that basic set of rules.

On top of that, when you do this:

this.elem.addEventListener('click', this.click);

You are just getting the this.click method and passing that method alone to addEventListener(). The value of this will be completely lost. It's as if you are doing this:

var m = this.click;     // m here is just a reference to Clicker.prototype.click
this.elem.addEventListener('click', m);

On top of this, .addEventListener() is specifically built to set it's own value of this when it calls the callback (to point this at the element creating the event).

So, you can use .bind() as shown above to force the proper value of this to be in place when your method is called.


For reference, you may find this description of the six ways that this is set for a function call in Javascript to be useful.


Other Options

I find .bind() to be the clearest way of defining this, but you could also use either a local anonymous function:

var self = this;
this.elem.addEventListener('click', function() {
    self.click();
});

or in ES6, an arrow function:

this.elem.addEventListener('click', () => this.click());

The arrow function will preserve the value of this for you automatically to avoid needing the self reference used in the prior example.

Share:
30,251
Andrew Luhring
Author by

Andrew Luhring

Updated on April 08, 2020

Comments

  • Andrew Luhring
    Andrew Luhring about 4 years

    For some reason I'm getting weird values for "this" in my es6 class...

    'use strict';
    class Clicker {
      constructor(element) {
        this.count = 0;
        this.elem = element;
        this.elem.addEventListener('click', this.click);
        
        // logs Clicker { count:0, elem: button#thing} as expected
        console.log(this);
      }
    
      click() {
        // logs <button id="thing">...</button> as unexpected...
        console.log(this);
        this.count++;
      }
    }
    
    
    var thing = document.getElementById('thing');
    var instance = new Clicker(thing);
    <button id="thing">Click me</button>

    Question:

    Why is the "this" inside of of the Clickers' click method referring to the dom node rather than ... itself?

    More importantly, how do I refer to Clickers' count property from within its' click method if I can't use "this" to do it?

  • Andrew Luhring
    Andrew Luhring about 8 years
    I knew it was something simple. expect your solution to be accepted in ~12 minutes.
  • jfriend00
    jfriend00 about 8 years
    @AndrewLuhring - Note, I added more explanation.
  • Swimburger
    Swimburger about 8 years
    On top of this answer you could also solve it by using this.elem.addEventListener('click', () => this.click()); I believe
  • jfriend00
    jfriend00 about 8 years
    @sniels - I added your option and one other to my answer. Good idea.
  • Aleksej Shovgenja
    Aleksej Shovgenja almost 8 years
    It seems like this this.elem.addEventListener('click', this.click.bind(this)); syntax don't let us to remove the listener, like it is another function. But i don't get it really. Somebody explain me why it is behaving like that?
  • jfriend00
    jfriend00 almost 7 years
    @AleksejBrume - It is another function - .bind() creates a wrapper function. If you want to use the same function to then call .removeEventListener(), then you need to do something like var fn = this.click.bind(this);` and then use fn for both .addEventListener() and .removeEventListener().
  • Noah
    Noah over 6 years
    Check out core-decorators on npm. import { autobind } from 'core-decorators'. Then in your class methods @autobind click () { ... }. Makes me very happy.