0

In the below piece of code, when sayAysncHi() is called on gs object, what is this bound to ? My understanding is that this is bound to the thing to the left of the dot (.) operator which in this case should be the GreetingService object gs. However in this case the this.name inside the sayAsyncHi fn is coming in a undefined - why is this so?

// file greeting_service.js
function GreetingService(name) {
  this.name = name;
  this.sayHi = function () {
    console.log(this);
    console.log(`Hi ${this.name}!`);
  }
  this.sayAsyncHi = function () {
    setTimeout(function (params) {
      console.log(this);
      console.log(`Hi ${this.name}!`);
    },2000);
  }
  this.sayBye = function () {
    console.log(`Bye ${this.name}!`);
  }
}

var gs = new GreetingService('Jon');
gs.sayHi(); // Hello Jon!
gs.sayAsyncHi(); // Hello undefined!

Output on running node greeting_service.js ( nodejs 6)

GreetingService {
  name: 'Jon',
  sayHi: [Function],
  sayAsyncHi: [Function],
  sayBye: [Function] }
Hi Jon!

Timeout {
  _called: true,
  _idleTimeout: 2000,
  _idlePrev: null,
  _idleNext: null,
  _idleStart: 389,
  _onTimeout: [Function],
  _repeat: null }
Hi undefined!

PS: with ES6 Arrow function the binding is correct as expected for the above Async call

user3206440
  • 3,707
  • 9
  • 48
  • 93
  • 2
    `() => {} ~= function(){}.bind(this)` – elclanrs Aug 21 '16 at 17:35
  • 1
    Yes, `this` is `gs` in `sayAsyncHi`. But in the timout you have another function, which has another `this`. – Oriol Aug 21 '16 at 17:38
  • Closure functions created with 'function' keyword creates own scope. So 'setTimeout(function()...{}) creates new scope. You can fix by using ES6 arraw functions or use '.bind(this)' to give your scope. – Maxali Aug 21 '16 at 17:44
  • @Oriol - regarding the another `this` inside the function in setTimeout, what is the value of `this` really in the above case ? – user3206440 Aug 22 '16 at 03:08
  • 1
    @user3206440 It's the window in browsers. I don't know in node, maybe the instance that will be returned. – Oriol Aug 22 '16 at 03:46

1 Answers1

1

A small fix (I added .bind(this)):

this.sayAsyncHi = function () {
  setTimeout(function (params) {
    console.log(this);
    console.log(`Hi ${this.name}!`);
  }.bind(this), 2000);
}

EDIT

A bit of explanation: in sayAsyncHi, you'll find the this variable has the value you expect. But the function you pass to setTimeout isn't bound to any value of this. You need to explicitly bind it if you want a specific value.

user94559
  • 54,841
  • 6
  • 85
  • 93
  • Async variant this.sayAsyncHi = function () { var self = this; setTimeout(function (params) { console.log(self); console.log(`Hi ${self.name}!`); }, 2000); } – Denis Shiryaev Aug 21 '16 at 17:44