What is this mysterious this keyword that keeps showing up? Let's find out and get really good at using it!
This is a companion discussion topic for the original entry at https://www.kirupa.com/javascript/this_that_arrow_functions_bind.htm
What is this mysterious this keyword that keeps showing up? Let's find out and get really good at using it!
You should add @senocular âs flow chart of thisâŚ.
But then you might have to change the title to:
WTF is âthisâ?!⌠an engineers guide.
Some comments:
let myObject = { name: "Iron Man", whatIsThis: function () { console.log(name); // won't work! } }; myObject.whatIsThis(); // undefined
You have to be careful with examples like these. What you end up getting is not undefined
, rather whatever the name
is of the current browser window/frame since name
is a global property in browsers. For example, if you pasted this code in a jsfiddle and ran it, youâd see the output of "result"
. In most other browser windows itâll usually be an empty string because that is the default of window.name. And if youâre not in a browser, running in some environment that doesnât have a global name
, youâd get an error.
// NodeJS
let myObject = {
name: "Iron Man",
whatIsThis: function () {
console.log(name);
}
};
myObject.whatIsThis(); // ReferenceError: name is not defined
The reason is that traditional functions donât behave in this seemingly logical way. They define their own value for this and that is always going to refer to the context they are being used in.
Not the context theyâre being used in, but how theyâre called. The only time they use the context theyâre being used in is when called in global, and that just so happens to be because default binding uses the global object. And, well, this kind of applies to modules too because their context is undefined
but modules are also always in strict mode which means the default binding will use undefined
instead of window. So in that case it technically matches too . In any function context, function calls would not refer to that context unless explicitly told to or using an arrow function that also happened to be defined in that same context (except then thereâs no binding, its just grabbing this
from the scope).
Our anonymous function inside our setInterval doesnât get created when our counter object is initialized. It gets created only when we call the startCounting method:
counter.startCounting();
This call lives in the context of the window object. When startCounting is invoked and the anonymous function is created, the this.initial call is looking for the value of initial on the window object.
This one is a little tricky and Iâm not quite sure about the wording here. Specifically
This call lives in the context of the window object.
While the call is happening in the global scope/context, it has no bearing on what the setInterval callback is doing. And yes, while it ends up looking for initial
on the window object, the blame for that is entirely on setInterval
. It defaults to calling callbacks specifically through window (or the Timeout object in Node) and not using the default binding. You can see the difference in strict mode when the default binding gives you a this
of undefined
but through setInterval
(and setTimeout
) you still get window.
"use strict";
function test (label) {
console.log(label, this)
}
test("normal")
setTimeout(test, 0, "timeout")
// normal undefined
// timeout Window { ... }
The timeout functions are effectively doing a callback.call(window)
in the background rather than calling the function normally. That, in turn, can cause some unexpected behavior when trying to explain the behavior of this
.
Beyond not having their own this value (which they inherit from their surroundings), arrow functions have no constructor or prototype properties.
While they have no prototype
property, they do have a constructor
. What they donât have their own of includes: this
, arguments
, super
, and new.target
.
They also donât support the bind, call, and apply methods.
They support these methods, but these methods canât be used to change the this
value in an arrow function.
const add = (a, b) => a + b
console.log(add.call(null, 1, 2)) // 3
When called, what bind creates is a new function known as a bound (or exotic ) function
Iâd be careful with the use of âexoticâ here. While bound functions are exotic not all exotic functions are necessarily bound. In fact arrays are exotic (exotic objects, not functions). Exotic values are simply values that have unique, internal behavior to them.
As weâve seen with the flowchart for this
itâs a messy, complicated topic
This is excellent feedback, sen! Iâll incorporate your comments into this article shortly.
:: Copyright KIRUPA 2024 //--