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