Every JavaScript developer, including myself, has been struggling in understanding how this
keyword works.
I've created a universal algorithm to help you determine the value of this
keyword in any situation.
While I made the algorithm as accessible as possible, I recommend reading it multiple times and understand the related terms.
Also, I'll show you a step-by-step evaluation of the algorithm for example situations. Finally, try the homework exercises by yourself!
Note: If you don't understand the algorithm from the first time: that's expected! Get back to the post later and try again until you crack it.
Ready? Let's begin!
1. this algorithm
The formal definition of ThisValueOfFunction(func, invocationType)
that returns this
value a function func
invoked in a certain way invocationType
.
ThisValueOfFunction(func, invocationType):
-
If
func
is a regular function, then-
If
invocationType
is as a constructor, then- let
newObject
be the newly constructed objectnewObject = new func()
return newObject
- let
-
Else if
invocationType
is indirectly, then- let
thisArg
be the argument offunc.call(thisArg)
orfunc.apply(thisArg)
return thisArg
- let
-
Else if
invocationType
is as a method, then- let
object
be the object upon whichfunc
is invoked onobject.func()
return object
- let
-
Else if
invocationType
is regular, then- If strict mode is enabled, then
return undefined
- Else
return globalObject
- If strict mode is enabled, then
-
-
Else if
func
is an arrow function, then- If
func
is defined in the outermost scope, thenreturn globalObject
- Else
- let
outerFunc
be the outer function offunc
return ThisValueOfFunction(outerFunc, outerInvocationType)
- let
- If
-
Else if
func
is a bound function of anoriginFunc
function, then- let
thisArg
be the argument offunc = originFunc.bind(thisArg)
return thisArg
- let
-
Else if
func
is aconstructor()
method inside of a classSomeClass
, then- let
instance
be the instance of the classinstance = new SomeClass()
return instance
- let
1.1 The terms used in the algorithm
The algorithm uses plenty of JavaScript terms. If you aren't familiar with something, expand and look at the explanation.
Arrow function
An arrow function is a function defined using the fat arrow syntax =>
. Example of an arrow function:
const sum = (number1, number2) => { return number1 + number2;}
Bound function
A bound function is a function created from invoking the method myFunc.bind(thisArg, arg1, ..., argN)
upon a function. Example of a bound function:
function originalFunction() { // ...}const boundFunction = originalFunction.bind({ prop: 'Value' });
Regular function
A regular function is a simple JavaScript function defined using function
keyword or using a shorthand definition on an object. Examples of regular functions:
function regularFunction(who) { return `Hello, ${who}!`;}const object = { anotherRegularFunction(who) { return `Good bye, ${who}!` }};
constructor()
constructor() is a special method inside of a class
that initializes the class instance.
class SomeClass() { constructor(prop) { this.prop = prop; }}
Outermost scope
The outermost scope is the top scope that doesn't have an outer scope.
// The outermost scopelet a = 1;function someFunction() { // someFunction() scope // Not the outermost scope let b = 1;}
Outer function
The outer function contains another function within its scope.
// outerFunction() is the outer function of myFunction()function outerFunction() { function myFunction() { }}
Global object
The global object is the object that always exists in the global scope. window
is the global object in a browser environment, global
in Node environment.
Invocation
Invocation of a function is just calling the function with some arguments.
function sum(number1, number2) { return number1 + number2;}sum(1, 3); // Invocationsum.call({}, 3, 4); // Invocationsum.apply({}, [5, 9]); // Invocationconst obj = { method() { return 'Some method'; }};obj.method(); // Invocationclass SomeClass { constructor(prop) { this.prop = prop; } }const instance = new SomeClass('Value'); // Invocation
Constructor invocation
Constructor invocation happens when a function or class is invoked using new
keyword.
function MyCat(name) { this.name = name;}const fluffy = new MyCat('Fluffy'); // Constructor invocationclass MyDog { constructor(name) { this.name = name; }}const rex = new MyDog('Rex'); // Constructor invocation
Indirect invocation
An indirect invocation of happens when a function is called using func.call(thisArg, ...)
or func.apply(thisArg, ...)
methods.
function sum(number1, number2) { return number1 + number2;}sum.call({}, 1, 2); // Indirect invocationsum.apply({}, 3, 5); // Indirect invocation
Method invocation
Method invocation happens when a function is invoked in a property accessor expression object.method()
.
const object = { greeting(who) { return `Hello, ${who}!` }};object.greeting('World'); // Method invocationobject['greeting']('World'); // Method invocation
Regular invocation
Regular invocation happens when the sole variable containing the function is used for invocation func(...)
.
function sum(number1, number2) { return number1 + number2;}sum(1, 4); // Regular invocation
Strict mode
Strict mode is a special mode imposed upon running JavaScript code having some special restrictions. The strict mode is enabled by adding 'use strict'
directive at the start of the script or the top of the function scope.
2. Examples
Example 1
const myFunc = () => { console.log(this); // logs `window`};myFunc();
ThisValueOfFunction(myFunc, "regular")
myFunc
is an arrow function: thus matching the point 2 in the algorithm. Also myFunc
is defined in the outermost scope, matching the point 2.1.
The point 2.1 of the algorithm says return globalObject
: meaning that this
value inside myFunc
is the global object — window
(in a browser environment).
Example 2
const object = { method() { console.log(this); // logs { method() {...} } } };object.method();
ThisValueOfFunction(object.method, "as a method")
method()
, while being a property of the object
, is a regular function. The point 1 of the algorithm is matched.
object.method()
is a method invocation because of the property accessor usage: thus the point 1.3 is matched.
Then, according to point 1.3, this
value inside method()
equals the owning object of the method invocation (object.method()
) — object
.
Example 3
function MyCat(name) { this.name = name; const getName = () => { console.log(this); // logs { name: 'Fluffy', getName() {...} } return this.name; } this.getName = getName;}const fluffy = new MyCat('Fluffy');fluffy.getName();
ThisValueOfFunction(getName, "as a method")
getName()
is an arrow function, thus the point 2 of the algorithm is applied. Then the point 2.2 matches, because MyCat
is the outer function of getName()
.
The point 2.2.2 says that this
value inside getName()
arrow function equals this
value of the outer function: MyCat
.
So, let's run the algorithm recursively again on MyCat
function — ThisValueOfFunction(MyCat, "as a constructor")
.
ThisValueOfFunction(MyCat, "as a constructor")
MyCat
is a regular function, thus the point 1 of the algorithm is applied.
Because MyCat
was invoked as a constructor new MyCat('Fluffy')
, the point 1.1 is applied. Finally, according to points 1.1.1 and 1.1.2, this
value inside MyCat
equals to the constructed object: fluffy
.
And, returning back to the arrow function's point 2.2.2, this
inside of the getName()
equals this
of the MyCat
— which is finally fluffy
.
3. Homework
The best way to understand the algorithm is by trying it yourself. Follow the 3 exercises in determining this
value.
Exercise 1
const myRegularFunc = function() { console.log(this); // logs ???};myRegularFunc();
How would the algorithm determine this
value inside myRegularFunc()
? Write the step-by-step evaluation.
Exercise 2
class MyCat { constructor(name) { this.name = name; console.log(this); // logs ??? }}const myCat = new MyCat('Lucy');
How would the algorithm determine this
value inside new MyCat('Lucy')
? Write the step-by-step evaluation.
Exercise 3
const object = { name: 'Batman', getName() { const arrow = () => { console.log(this); // logs ??? return this.name; }; return arrow(); };}object.getName();
How would the algorithm determine this
value inside arrow()
function? Write the step-by-step evaluation.
4. Summary
In this post, I presented a universal algorithm to determine the value of this
inside of an invoked function.
While the algorithm might be challenging at first, if you understand the step-by-step examples, you will realize how easy is to apply the algorithm.
Struggle applying the algorithm for a certain situation? Describe your case in a comment below!