0
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?

  • You create a new scope with the function – Phix Sep 28 '21 at 18:58
  • @Phix you mean in nodejs, a new scope is created with the callback function? But isn't it the same case in a browser? – user14427500 Sep 28 '21 at 19:00
  • @Phix that has no relevance for the value of `this`. – VLAZ Sep 28 '21 at 19:01
  • 1
    you generally shouldn't assume what `this` will be unless it's being specifically set by whatever is calling the function, or if the function is bound. In this case it's a function that isn't bound and being called by setTimeout, so `this` isn't something that should be used/relied on within that function. – Kevin B Sep 28 '21 at 19:01
  • If it was an arrow function, and therefore bound to the containing context, you could then use it knowing what it is (assuming `this` in the parent scope is similarly well defined) – Kevin B Sep 28 '21 at 19:04
  • [This](https://stackoverflow.com/a/43630118/14632179) might answer your question. – soothsayer Sep 28 '21 at 19:04
  • 1
    TLDR i wouldn't suggest relying on `this` being window or the module or whatever the environment you're in defines `this` as in that location, instead be explicit when possible. – Kevin B Sep 28 '21 at 19:06
  • If you're not using a Javascript construct that explicitly sets `this` to a desired value, then don't use it at all. You're just playing roulette when doing so. If you want the reference a global, then use a specific global reference (`window.x` or `global.x`). – jfriend00 Sep 28 '21 at 19:40
  • Does this answer your question? [Different behaviour of setTimeout in nodejs and Chrome](https://stackoverflow.com/questions/53529250/different-behaviour-of-settimeout-in-nodejs-and-chrome) – VLAZ Sep 29 '21 at 05:16

3 Answers3

1

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.

jfriend00
  • 683,504
  • 96
  • 985
  • 979
0

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.

NicoWheat
  • 2,157
  • 2
  • 26
  • 34
0

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

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

abubakri olaitan
  • 220
  • 3
  • 11
  • "*fat arrow is a syntactic sugar of bind*" no, it is not. It's a completely different construct. Binding a function at creation time is *similar* to using an arrow function but definitely not the same. Among the differences are that an arrow function doesn't get its own `arguments` at execution (same as how it doesn't get its own `this`) and it is also not constructable. See also [Are 'Arrow Functions' and 'Functions' equivalent / interchangeable?](https://stackoverflow.com/q/34361379). Arrow functions are not just syntactic sugar. – VLAZ Sep 29 '21 at 05:14