1

I'm learning JS classes and have run into an issue. I cannot figure out how to self-reference my class variables (set in the constructor) when I lose this context, like after a Promise.

For example:

class myClass{
    constructor(name) {
        this.name = name;
        doSomething().then(doSomethingElse)
    }

    doSomething(){
        return new Promise((resolve, reject)=>{
            resolve("myAsyncName")
        })
    }

    doSomethingElse(value){
        this.name = value;
    }
}

In the doSomethingElse function, I'll get an error that this is undefined or cannot set name on it. I have tried using setting self = this in the constructor which works but breaks if I use the class more than once. I simply need to be able to reference variables set in my constructor. I've tried searching many posts and reading a plethora of articles on how to use this, and bind, but I cannot find the pattern I need to use in this situation.

Ryan Peterson
  • 132
  • 1
  • 9

2 Answers2

5

This is due to the context that doSomethingElse is being called from. In your example, the this used in doSomethingElse will be referencing the context of then, not your class.

There are a couple of ways to get around this problem:

1. Binding the method to the class itself at all times.

constructor(name) {
  this.name = name;

  // binding the `this` context
  this.doSomethingElse = this.doSomethingElse.bind(this);
  // the this in doSomethingElse will always refer the class now
  doSomething().then(doSomethingElse)
}

doSomethingElse() {

2. Using the ES6 fat arrow which lexically bind their context

constructor(name) {
  this.name = name;
  doSomething().then(() => this.doSomethingElse())
}

3. Using the transform-class-properties for babel (for advanced users)

Now this one is not part of the official ecmascript spec yet, so I do not encourage using it. I am going to assume that you are, or at some point will be, using babel. If not, that is totally okay!

With this plugin you should be able to write something like:

class myClass{
  constructor(name) {
    this.name = name;
    // no need to change anything here
    doSomething().then(doSomethingElse)
  }

  doSomething(){
    return new Promise((resolve, reject)=>{
        resolve("myAsyncName")
    })
  }

  // notice the fat arrow syntax here. `this` will always be
  // referencing myClass
  doSomethingElse = (value) => {
    this.name = value;
  }
}

I highly recommend reading Kyle Simpson's You Don't Know JS: this & object prototypes on this topic.

Good luck with your journey learning Javascript!

Waseem
  • 651
  • 1
  • 5
  • 15
0

You need to bind your doSomethingElse function to this

i.e

this.doSomething().then(this.doSomethingElse.bind(this))

Alternatively, you can use arrow function in your .then

i.e

this.doSomething().then(value => this.doSomethingElse(value));
JohanP
  • 5,252
  • 2
  • 24
  • 34