0

How can I call an object method inside setTimeout without losing the this binding?

const bob = {
  name: 'Bob',
  greet: function () {
    console.log(`Hi, I'm ${this.name}`)
  }
};

bob.greet();                   // Hi, I'm Bob
setTimeout(bob.greet, 500);    // Hi, I'm

I know that in the setTimeout call, this is bound to the Window object.

Bobby Wan-Kenobi
  • 885
  • 9
  • 18
  • 1
    Does this answer your question? [How to access the correct \`this\` inside a callback?](https://stackoverflow.com/questions/20279484/how-to-access-the-correct-this-inside-a-callback) – A_A Jun 12 '21 at 18:08
  • Hmm, I don't see it. I'm not using constructors, just an object literal. – Bobby Wan-Kenobi Jun 12 '21 at 18:10
  • It is the same principle. You want to refer to `this` (`this.name`) from within a callback (`bob.greet`). The solutions there also work for you – A_A Jun 12 '21 at 18:12
  • Bind the `this` scope with [`.bind`](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Function/bind) to the method object: `bob.greet.bind(bob)` – Marc Jun 12 '21 at 18:12
  • @Marc where do I put that? – Bobby Wan-Kenobi Jun 12 '21 at 18:13
  • See the link. bind returns a function, wich scope is bind to the object you pass to it. `setTimeout(bob.greet.bind(bob), 500);` – Marc Jun 12 '21 at 18:14
  • @Marc if you put it in an answer I'll accept it – Bobby Wan-Kenobi Jun 12 '21 at 18:15

3 Answers3

2

You have a scoping probleme here.

Use the .bind method to set the this scope to the bob object:

const bob = {
  name: 'Bob',
  greet: function () {
    console.log(`Hi, I'm ${this.name}`)
  }
};

bob.greet();                   // Hi, I'm Bob
setTimeout(bob.greet.bind(bob), 500);    // Hi, I'm

.bind returns a new function with the correct scope.

As an alternative solution, you can use a wrapper callback:

setTimeout(() => {
  bob.greet();
}, 500);

Notice the arrow function, which has no this scope: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions

Marc
  • 2,920
  • 3
  • 14
  • 30
2

You can use a wrapper function like this:

const bob = {
  name: 'Bob',
  greet: function () {
    console.log(`Hi, I'm ${this.name}`)
  }
};

bob.greet();
setTimeout(() => bob.greet(), 500);

MDN explains exactly this perfectly here

zb22
  • 3,126
  • 3
  • 19
  • 34
0

Quoting from [MDN] https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/this

" In most cases, the value of this is determined by how a function is called (runtime binding). It can't be set by assignment during execution, and it may be different each time the function is called. ES5 introduced the bind() method to set the value of a function's this regardless of how it's called, and ES2015 introduced arrow functions which don't provide their own this binding (it retains the this value of the enclosing lexical context)."

In addition to the method provided by Marc https://stackoverflow.com/a/67951749/14550472

an arrow function can also help

setTimeout(()=>{bob.greet()}, 500);

nps
  • 46
  • 4