Notes on this
July 15, 2021
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.