1

Consider the following example:

class Test {
  constructor() {
    this.x = 42;
  }
  
  someMethod() {
    console.log(this.x);
  }
}

testBtn.x = -42;

var TestInstance = new Test()

testBtn.addEventListener('click', TestInstance.someMethod);
<button id="testBtn" type="button">Click me</button>

Clicking the button will log -42 to the console.

I know I can fix this by explicitly binding TestInstance to the event handler reference:

class Test {
  constructor() {
    this.x = 42;
  }
  
  someMethod() {
    console.log(this.x);
  }
}

testBtn.x = -42;

var TestInstance = new Test()

testBtn.addEventListener('click', TestInstance.someMethod.bind(TestInstance));
<button id="testBtn" type="button">Click me</button>

The downside of this being that I can no longer remove that event listener.

I know this can be fixed, too:

class Test {
  constructor() {
    this.x = 42;
    this.someMethod = this._someMethod.bind(this);
  }
  
  _someMethod() {
    console.log(this.x);
    testBtn.removeEventListener('click', this.someMethod);
  }
}

testBtn.x = -42;

var TestInstance = new Test()

testBtn.addEventListener('click', TestInstance.someMethod);
<button id="testBtn" type="button">Click me</button>

But this would basically mean that to expose the class API on each method that needs this to point to the instance, I'd have to create a bound version of that method in the constructor.

Is that the intended way this works, or am I missing a different and hopefully better approach?

Edit: So I'll go with this until the public class field syntax proposal has passed the proposal status:

class Test {
  constructor() {
    this.x = 42;
    
    const boundMethodNames = ['someMethod'];
    boundMethodNames.forEach(method => this[method] = this[method].bind(this));
  }
  
  someMethod() {
    console.log(this.x);
    testBtn.removeEventListener('click', this.someMethod);
  }
}

testBtn.x = -42;

var TestInstance = new Test();

testBtn.addEventListener('click', TestInstance.someMethod);
<button id="testBtn" type="button">Click me</button>
connexo
  • 53,704
  • 14
  • 91
  • 128
  • So is the issue that you don't want `_someMethod` to be seen from the outside of `Test`, and that you don't want to `.bind` in the constructor since there may not be a listener added (no need for a bound function)? I'm not sure what exactly you're looking for when you say "better approach" – CertainPerformance Mar 06 '19 at 09:58
  • 1
    1) You don't need to bind the method under a different name, `this.foo = this.foo.bind(this)` will do just fine. 2) See [the duplicate](https://stackoverflow.com/questions/31362292/how-to-use-arrow-functions-public-class-fields-as-class-methods) for an experimental shorthand for that. 3) No, you're not really missing much apart from this. – deceze Mar 06 '19 at 10:03
  • Note that the class field syntax is basically doing the exact same thing you're doing right now, of binding the method in the constructor – CertainPerformance Mar 06 '19 at 10:05
  • @deceze But that would mean I'd have to do this explicit binding in the constructor on every method that I want to be able to pass a reference to some other place. That feels less than ideal, which is why I assumed there's got to be something crucial I'm missing. – connexo Mar 06 '19 at 10:07
  • No, again, you're not missing anything. Yes, it's annoying, which is why the above mentioned experimental shorthand syntax was added. – deceze Mar 06 '19 at 10:08
  • @CertainPerformance When developing such a class I need to make sure `this` **always points to the instance of the class** no matter who calls a method, especially when passing a reference of an instance method. – connexo Mar 06 '19 at 10:09

0 Answers0