Home > Enterprise >  setTimeout & "this" keyword in nodejs
setTimeout & "this" keyword in nodejs

Time:09-29

const person = {
  talk() {
    setTimeout(function () {
      console.log(this);
    }, 1000);
  },
};

person.talk();

I know when you call a stand-alone function in javascript, "this" will refer to the global object (i.e., the window object in browser, or the global object in nodejs).

Since a callback function is a stand-alone function, I expect it will print window/global in the above example.

However, when I test it in browser, it did return the window object. But when I run it in node, it returns a Timeout object rather than the global object. What is the reason behind it?

CodePudding user response:

The answer to your question can be found here:

Different behaviour of setTimeout in nodejs and Chrome.

In short, Node's setTimeout is a different function than a browser's setTimeout, despite having similar functionality and identical name. I do not know the underlying reason they were created to have different this references, but they do.

CodePudding user response:

setTimeout() is not actually part of the Javascript standard - it is supplied by the host environment.

In the nodejs implementation, a timer is an actual object and nodejs calls the timer callback by calling a method on that object like this:

timer._onTimeout();

where this is set in the contructor of the object like this:

this._onTimeout = callback;

to the timer callback. Thus, this will be the timer object (due to the method call). You can examine the nodejs timer object source yourself here.

One of the reasons for nodejs to turn a timer ID into an object is that it also has other methods such as .ref() and .unref() which are cleaner to implement and expose to the programmer if the timer handle is an actual object rather than adding more functions to the global namespace.

The browser has a different implementation that calls the callback as a normal function. And, in Javascript (when not in strict mode) calling a normal function sets this to the global object in the function.


As I said in the comments, you should NOT rely on any value of this that is not explicitly documented to be what you want or controlled by the way you have called things. To do so is just playing roulette.

If you want to explicitly reference the global object, then I'd suggest you just specifically refer to the global object as window.x or global.x depending upon your environment.

CodePudding user response:

The reason behind this is that this binding is determined based on where it was called not where it is declared.

There are four rules according to You don't Know JS books

Default Binding example

let world = "hello world"
function hello() {
 console.log(this.world)
 console.log(this)
}

hello() // prints hello world and global object 
//the call site is on the global environment

Implicit Binding example

let object1 = {
  world: "I am being called from object1 site ",
  hello: hello
}

let object2 = {
  world: "I am in 2",
  object1: object1
}

object2.object1.hello() //will print " I am being called from object1 site because of the taking the object that is closest level to the function and that is objec1"

obj.hello() // print "I am being called from object1 site and object1 properties

Explicit Binding This occurs when you are 'explicit' about the object you want this to refer to and in this case you use call, apply and bind (which is more stronger in binding this to expected object)

Example

hello.call(object1) in this case this will refer to object1. In an event where call or apply does not work you can use bind to make a hard bind .That is why you would see this pattern a lot Let say you want to refer to the person object

const person = {
  talk() {
    setTimeout(
      function () {
        console.log(this);
      }.bind(this),
      1000
    );
  },
};

person.talk(); // `this` will refer to the person object

Let say you want to refer to global object in Node

let globalObject = globalThis;
const person = {
  talk() {
    setTimeout(
      function () {
        console.log(this);
      }.bind(globalObject),
      1000
    );
  },
};

person.talk(); // this will refer to global object

fat arrow is a syntactic sugar of bind

New Binding: this is using keyword new to create an object

Example let b = new life()

To determine this in code you have to know how it was used in the call site and they take precedence over in this order: new binding > explicit binding > implicit binding > default binding

  • Related