No Time Dad

A blog about web development written by a busy dad

Notes on this

I came up with so many clever titles for this post. Ones like “What is this?”, “Is this the end?”, and “You don’t know this”. I eventually decided that “Notes on this” was best, and I should leave the dad jokes out of this.

Below are notes that attempt to explain JavaScript’s this keyword from the top down. It’s not going to explore every corner, but it should provide a general overview.

Formal definition of this

In JavaScript, the keyword this is a property of an execution context that, when in non-strict mode, is a reference to an object, and when in strict mode can be any value.

Pulling this formal definition apart, what is an execution context?

An execution context is the environment where the code is running, created when the code is executed.

Common types of execution contexts

global

The global execution context is the default context where code execution by the JavaScript engine begins. It includes all code not in a function.

In the code example below, the variable hello is part of the global execution context because it’s not defined inside of a function. In this example, this refers to the global execution context, so the variable can be accessed via this.hello and directly via hello.

// myFile.js
var hello = 'hello';
console.log(this.hello); // "hello"
console.log(hello); // "hello"

function

The function execution context is a context created by the JavaScript engine for function calls, in which a new execution context is created for that function.

Things get interesting in the function execution context. The value of this depends on how the function is called.

If the given function is in non-strict mode and this is not set by the function call, the value of this will default to the global execution context.

// myFile.js
var hello = 'hello';
console.log(this.hello); // "hello"

function myFunc() {
	return this.hello;
}

console.log(myFunc()) // "hello"

So, if the given function is in strict mode, this will be undefined unless it’s set.

// myFile.js
var hello = 'hello';
console.log(this.hello); // "hello"

function myFunc() {
  "use strict"
	return this.hello;
}

console.log(myFunc()) // undefined (or TypeError)

Looking back at the formal definition of this, what is strict mode and non-strict mode?

Strict mode and non-strict mode

Strict mode lets you enable a restricted variant of JavaScript.

Strict mode does a few different things. The first is that it gets rid of silent errors, and instead throws errors.

// myFile.js
'use strict';

hello = 'hello'; // ReferenceError
console.log(hello); // Not executed due to above error
// myFile.js
hello = 'hello';
console.log(hello); // "hello"

Another example of getting rid of silent errors:

'use strict';

var undefined = 'hello'; // TypeError
console.log(undefined); // Not executed due to above error
var undefined = 'hello';
console.log(undefined); // undefined

In addition to removing silent errors, strict mode also resolves problems to help the JavaScript engine perform more optimizations. An interesting result of this is that sometimes, identical code can run faster in strict mode than non-strict mode. Lastly, strict mode does not allow syntax from future ECMAScript versions to be used.

The opposite of strict mode is known as “sloppy mode” or non-strict mode.

Arrow functions

Arrow functions do not have their own bindings to this, they instead inherit from the parent scope. In the example below, the function will return the global context for this.

const myFunc = () => this;
console.log(myFunc()); // global context is logged

Similar to the above example, the arrow function will use the global execution context this.

var name = 'Bob';
const myFunc = () => console.log(this.name);
myFunc(); // Bob

In the next example, the function returns Bob is hosting a party, where the arrow function returns undefined is hosting a party. Because the arrow function does not have this it’ll look in the global context because that’s where it was called from, and this.host (in the below example) is undefined in the global context.

const parent = {
    host: "Bob",
    party: function () { return `${this.host} is hosting a party` },
};
console.log(parent.party()); // Bob is hosting a party
const parent = {
    host: "Bob",
    party: () => `${this.host} is hosting a party`,
};
console.log(parent.party()); // undefined is hosting a party

In a React component class, it’s common to have to bind method functions to the class instance of this. Which needs to happen because functions have their own this, so in order to reference the class this it needs to be bound to it. Arrow function methods do not have this issue because they don’t have their own this and instead inherit the next level up parent’s this context.