Explain how `this` works in JavaScript
The this
keyword always refers to the current context of a function or script.
this
is a confusing topic for most of us (pun intended), but it doesn't have to be. All you need to do is remember some rules.
Following rules, in order of precedence, dictate how value of this
is determined during runtime:
Usage in a Function constructor
If a function is called with the new
keyword, this
inside the function refers to the newly created object instance.
function Book(title) { console.log(this); this.title = title; console.log(this); } const book1 = new Book("Jungle Book"); // {} // { title: 'Jungle Book' } console.log(book1.name); // 'Jungle Book'
Explicitly binding this
call()
, apply()
or bind()
can be used to explicitly set the value of this
inside a function.
call()
andapply()
are used when a function needs to be invoked immediately.bind()
returns a new function to be used later.
const obj = { name: "Ben" }; function sayHello() { console.log(`Hello, ${this.name}!`); } const sayHelloToBen = sayHello.bind(obj); sayHelloToBen(); // Hello, Ben! sayHello.call(obj); // Hello, Ben! sayHello.apply(obj); // Hello, Ben!
Usage in a method call
If a function is a method of an object, then this
refers to the object.
const person = { name: "Ben", logThis: function () { console.log(this); }, }; person.logThis(); // { name: 'Ben', logThis: fn() }
Usage in a Regular function call (Free function invocation)
If a function is invoked in the global context, then this
refers to the global object(in non-strict mode) or undefined
(in strict mode).
For browsers, the global object is window
.
// Browser function showThis() { console.log(this); } showThis(); // Window { open: fn, alert: fn, ... }
When a function is declared in the global context, it becomes a property of the window
object. Calling window.showThis()
would yield the same result. This is why it's an implicit method invocation. (Refer the rule above this one)
If multiple rules apply, then the rule with higher precedence will apply.
const obj1 = { value: 1, showThis: function () { console.log(this); }, }; const obj2 = { value: 2 }; obj1.showThis.call(obj2); // { value: 2 }
In the example above, two rules are being applied: Method invocation and Explicit binding. As explicit binding has higher precedence, it gets to set the value of this
.
Arrow functions
Arrow functions don't follow the rules stated above as they don't have a this
value of their own. They pick the this
value from the parent scope.
const person = { name: "Ben", showThis: () => { console.log(this); }, showThisWrapped: function () { const arrowFn = () => console.log(this); arrowFn(); }, }; person.showThis(); // Window { open: fn, alert: fn, ... } person.showThisWrapped(); // { name: 'Ben', showThis: fn, showThisWrapped: fn }
This is why you should avoid using arrow functions for event listeners. Event listeners bind the HTML element to the this
value but if the handler is an arrow function, it is not possible.
document.querySelector("#cta").addEventListener("click", function () { console.log(this); // HTMLButtonElement }); document.querySelector("#cancel").addEventListener("click", () => { console.log(this); // Window });
Summary
- In a function constructor(
new Person()
),this
will be the newly created object instance. - If explicitly bound using
bind()
,call()
orapply()
,this
inside the function will be set to the value provided. - Inside a method,
this
is set to the object that method is a property of. - Inside a free function invocation,
this
points to global object(non-strict mode) orundefined
(strict mode). - If multiple rules apply, the rule stated first(1-4) will apply.
- Arrow functions don't have their own
this
. Its value is picked from the parent scope.