2

jdfiddle: https://jsfiddle.net/2khtof8s/1/

class TestClass {
    doSomething(){
        return Promise.resolve(this.someFunction('hello'))
    }

    someFunction( testVar ){
        return testVar;
    }
}

let instance = new TestClass();

// throws error
Promise.resolve()
    .then(instance.doSomething)
    .then(console.log)
    .catch(console.error);

// works
Promise.resolve()
    .then(() => instance.doSomething())
    .then(console.log);

// works
Promise.resolve()
    .then(function(){return instance.doSomething()})
    .then(console.log);

// works
function someFunc(){
    return instance.doSomething();
}

Promise.resolve()
    .then(someFunc)
    .then(console.log);

the first Promise.resolve chain errors out with Uncaught (in promise) TypeError: Cannot read property 'someFunction' of undefined - we can't seem to understand why, does anyone have any insight into this behaviour? As best we can tell, it seems like there shouldn't be a difference

Eva
  • 4,859
  • 3
  • 20
  • 26
  • Possible duplicate of [How to access the correct \`this\` inside a callback?](http://stackoverflow.com/questions/20279484/how-to-access-the-correct-this-inside-a-callback) – Heretic Monkey Jan 25 '17 at 19:30

2 Answers2

4

The context is lost when you pass a reference to the instance's function.

instance.doSomething // just a reference

is just a reference with no context. In order to do what you are trying to do, you need to bind the context to the reference:

instance.doSomething.bind(instance)

This might help to think of it this way:

When you reference instance.doSomething, think of it as saying, "I am the instance, this is what doSomething looks like, and what I would use if you invoked me."

The difference is referencing vs calling.

Reference:

instance.doSomething

Calling: (Maintains context):

instance.doSomething();

class TestClass {
  doSomething() {
    return Promise.resolve(this.someFunction('hello'))
  }

  someFunction(testVar) {
    return testVar;
  }
}

let instance = new TestClass();

// No longer throws error
Promise.resolve()
  .then(instance.doSomething.bind(instance))
  .then(console.log)
  .catch(console.error);
KevBot
  • 17,900
  • 5
  • 50
  • 68
  • why then do the other forms not require a bind? – Eva Jan 25 '17 at 19:20
  • @Eva Because they don't reference `this`. – Dave Newton Jan 25 '17 at 19:21
  • @Eva, I've made some edits trying to explain the difference. But what is comes down to is that one is a reference to the function it "would" use when invoked, and the other is a call to that function still using the context. – KevBot Jan 25 '17 at 19:24
  • @KevBot thanks a lot - that helps, still think it feels counter-intuitive though since I would have thought `instance` was instantiated and thus the reference would have scope :( – Eva Jan 25 '17 at 19:27
0

This does not work for the same reason the sample below throws an error. If you assign a function that is a property of an object to another variable, it looses its context.

class Test {
  constructor() {
    this.foo = 'bar';
  }
  
  print() {
    console.log(this.foo);
  }
}

const test = new Test();

test.print();      // "bar"

const standalonePrint = test.print;
standalonePrint(); // throws

You can solve this by manually binding the function's context:

class Test {
  constructor() {
    this.foo = 'bar';
  }

  print() {
    console.log(this.foo);
  }
}

const test = new Test();

const standalonePrint = test.print;
const boundPrint = standalonePrint.bind(test);
boundPrint(); // "bar"
TimoStaudinger
  • 41,396
  • 16
  • 88
  • 94