I was learning about the this
keyword and how it means different things with regards to regular functions vs ES6 arrow functions and function expressions and I came across something odd when trying to run the following code in Chrome, Deno and Node. So I prepared following:
Example:
function foo(n) { console.log("***Begin Foo****") console.log(`n = ${n}nthis = ${this}nthis.count = ${this.count}`) console.log("****End Foo****") this.count++; } var count = 1; for (let i = 0; i < 5 ; ++i) { foo(i) } console.log("From global this.count = "+this.count) console.log(this)
Deno output:
PS E:webdevjs_scratchspace> deno run .another_this.js ***Begin Foo**** error: Uncaught TypeError: Cannot read property 'count' of undefined console.log(`n = ${n}nthis = ${this}nthis.count = ${this.count}`) ^ at foo (file:///E:/webdev/js_scratchspace/another_this.js:24:64) at file:///E:/webdev/js_scratchspace/another_this.js:31:5
Node output:
PS E:webdevjs_scratchspace> node .another_this.js ***Begin Foo**** n = 0 this = [object global] this.count = undefined ****End Foo**** ***Begin Foo**** n = 1 this = [object global] this.count = NaN ****End Foo**** ***Begin Foo**** n = 2 this = [object global] this.count = NaN ****End Foo**** ***Begin Foo**** n = 3 this = [object global] this.count = NaN ****End Foo**** ***Begin Foo**** n = 4 this = [object global] this.count = NaN ****End Foo**** From global this.count = undefined {}
Chrome output:
***Begin Foo**** n = 0 this = [object Window] this.count = 1 ****End Foo**** ***Begin Foo**** n = 1 this = [object Window] this.count = 2 ****End Foo**** ***Begin Foo**** n = 2 this = [object Window] this.count = 3 ****End Foo**** ***Begin Foo**** n = 3 this = [object Window] this.count = 4 ****End Foo**** ***Begin Foo**** n = 4 this = [object Window] this.count = 5 ****End Foo**** From global this.count = 6 Window {window: Window, self: Window, document: document, name: '', location: Location, …}
According to my understanding of this, where for arrow functions this
has no explicit binding and refers to the scope’s this
in which the arrow function was defined, whereas for regular functions this
refers to the context from which it was invoked, Chrome’s output seems to make the most sense to me. I don’t understand why for example, Node would not recognize the global object as this
. I am least bothered by Deno’s output as I guess I may not understand what it’s trying to do exactly.
Can someone explain why Node, Deno and Chrome are giving me different outputs?
Advertisement
Answer
Three different
this
behaviours for three different JS engines
That’s a misleading way of putting it. You have three different JS environments, but they’re all using the same engine.
I am befuddled by Node giving me
this = {}
.
That’s not what it’s giving you: this = [object global]
.
What you’re not seeing in Node is var count
showing up as this.count
. One way you’d get that behavior (I don’t know whether that’s what Node is doing) is by wrapping the entire code in an IIFE. If you do:
(function() { /* YOUR CODE HERE... */ })();
in Chrome, you’ll see the same behavior, because then var count
is just a function-local variable.
And as @Barmar said, you’d get Deno’s behavior by defaulting to strict mode (in addition to wrapping the code in an IIFE).
Conclusion: Relying on this
in the global scope is not a great idea. Try to use this
only for methods that will be called on objects (such as if you have foo.bar()
anywhere, then the body of bar() {...}
may use this
to refer to foo
).