Friday, 16 February, 2018 UTC


Summary

It’s easy to forget the rules around what JavaScript’s this keyword points to in different scenarios. This post aims to provide a quick refresher and reference to quickly grasp the different possibilities.
You'll almost always use this in the context of a function, but just remember that if this is used in the global context, then it points to the global object (e.g.: window in the browser and global in Node.js).
Four Rules
The value of this differs depending on how a function is invoked (the call site), so we can’t know the value of this just by looking at the function itself, but we need to know the context in which the function is invoked.
There are 4 rules to keep in mind. Let’s quickly review these rules.
Simple Function Call
tl;dr: this is the global object in non-strict mode, and undefined in strict mode.

In the case of a simple function call, in non-strict mode this will default to the global object:
function ghost() { console.log(this.boo); } ghost(); // undefined var boo = '👻'; ghost(); // 👻
In the same scenario, but in strict mode, this will be undefined. Our example errors-out because we can’t access boo on undefined:
'use strict'; function ghost() { console.log(this.boo); } ghost(); // TypeError: this is undefined // the rest is not executed var boo = '👻'; ghost();

As a side note, variables declared with let or const at the global level are not stored in the global object, but instead in an inaccessible declarative environment record, so our previous example gives us a different result when using let:
function ghost() { console.log(this.boo); } ghost(); // undefined let boo = '👻'; ghost(); // undefined window.boo = '👻'; ghost(); // 👻
Implicit Binding
tl;dr: this points to the object on which the function is called (what’s to the left of the period when the function is called).

This rule will apply for the majority of cases in your day-to-day code and applies when calling a method on an object:
let myGhost = { name: 'Casper', boo: '👻 Boo!!', ghost: function () { console.log(this.boo); } } myGhost.ghost(); // 👻 Boo!!
Note also that we get the same result if our object only contains a reference to the function:
function ghost() { console.log(this.boo); } let myGhost = { name: 'Casper', boo: '👻 Boo!!', ghost: ghost } myGhost.ghost(); // 👻 Boo!!
Explicit Binding
tl;dr: We can explicitly tell the JavaScript engine to set this to point to a certain value using call, apply or bind.

call and apply can be used to invoke a function with a specific value for this:
function ghost() { console.log(this.boo); } let myGhost = { name: 'Casper', boo: '👻 Boo!!', } ghost.call(myGhost); // 👻 Boo!!
Both call and apply accomplish the same task, and the first argument to both should be what this points to. The difference is only apparent if additional arguments need to be passed to the invoked function. With call, the additional arguments are passed as a normal comma-separated list of arguments, and with apply an array of arguments can be passed-in.

bind is used to create a new function that’s permanently bound to a this value. In the following example, we create a new function that has its this permanently bound to myGhost and re-assign ghost to that new permanently bound function:
function ghost() { console.log(this.boo); } let myGhost = { name: 'Casper', boo: '👻 Boo!!', } ghost = ghost.bind(myGhost) ghost(); // 👻 Boo!!
new Binding
tl;dr: Using the new keyword constructs a new object, and this points it.

When a function is invoked as a constructor function using the new keyword, this points to the new object that’s created:
function Ghost(name) { this.name = name; } let myGhost = new Ghost('Casper the friendly ghost'); console.log(myGhost.name); // Casper the friendly ghost
Bonus Rule: Arrow Functions
With arrow functions, this keeps the same value as its parent scope.
For example, here this in the arrow function keeps the same value as this in its enclosing Ghost function:
function Ghost(boo) { this.boo = boo; this.booUpperCase = () => { return this.boo.toUpperCase(); } } const myGhost = new Ghost('boo!!'); console.log(myGhost.boo); // boo!! console.log(myGhost.booUpperCase()); // BOO!!