Original post: https://www.ycmjason.com/blog/2018/06/15.html
- this article assumes 'use strict' in all context
- this article also assumes some knowledge about functions but still a bit confused
this
in Javascript is the probably most magical keyword in the programming world. It's unpredictable nature has reached to an unprecedented level.
However, it is essential to understand it fully if you wish to become a master of Javascript. So let me try to explain you what is this
. (if it doesn't work, well, at least I tried.)
Functions
Starting with functions. In this article, I would like to put functions into 3 different categories.
- Normal functions
- Arrow functions
- Bound functions
Normal functions
I define normal functions as any function created with...
// function declaration
function magic() {
...
}
// function expression
const magic = function() {
...
};
// (or if you hate your life)
// function constructor
const magic = new Function('...');
Arrow functions
Arrow functions are basically the ES6 arrow functions:
const magic = () => {
...
};
Bound functions
Bound functions can be created by calling Function.prototype.bind
on a normal functions.
// magic is created with function declaration/expression/constructor
const bound = magic.bind(...);
Ways to call a function
Now let's say we have a function f
(any category). There are 2 ways to call it.
- Implicit (direct) calls
- Explicit calls
Implicit (direct) calls
Implicit (direct) calls are boring:
/* f is defined */
// direct call
f();
// or attach it to an object and call it
const obj = {};
obj.fn = f;
obj.fn();
Explicit call
Explicit calls are more interesting. You can call your function with Function.prototype.call
or Function.prototype.apply
.
/* f is defined */
// Function.prototype.call
f.call(...);
// Function.prototype.apply
f.apply(...);
Quick recap
Let's do a quick recap, we have 3 categories of functions:
- Normal functions - created with function declaration/expression/constructor
- Arrow functions -
() => {...}
- Bound functions - created with
f.bind(...)
And 2 ways to call a function:
- Implicit (direct) calls -
f()
orobj.f()
- Explicit calls -
f.call(...)
orf.apply(...)
This means we have 6 different scenarios.
- Normal functions + Implicit (direct) calls
- Normal functions + Explicit calls
- Arrow functions + Implicit (direct) calls
- Arrow functions + Explicit calls
- Bound functions + Implicit (direct) calls
- Bound functions + Explicit calls
Don't panic, it is not that scary.
In fact, arrow functions and bound functions do not care about implicit/explicit calls. So this reduces down to only 4 scenarios:
- Normal functions + Implicit (direct) calls
- Normal functions + Explicit calls
- Arrow functions
- Bound functions
Procedure to find this
Below is the procedure to find the binding of this
in function f
:
Exercises!
Given magic
defined as follows:
'use strict';
const magic = function() {
// a. what is `this`?
console.log(this);
const cool = () => {
// b. what is `this`?
console.log(this);
};
cool();
};
// QUESTION 1
magic();
// QUESTION 2
const apple = { name: 'apple' };
apple.magic = magic;
apple.magic();
// QUESTION 3
const orange = { name: 'orange' };
magic.call(orange);
QUESTION 1.a
Following flow chart, we want to find this
in magic
.
- Category of
magic
is normal function -
magic
is called implicitly (directly) -
magic
is called withmagic()
- So
this
=undefined
!!!
QUESTION 1.b
Following flow chart, we want to find this
in cool
.
- Category of
cool
is arrow function - From QUESTION 1.b, we know
magic
'sthis
isundefined
-
cool
's definer ismagic
- So
this
=magic
'sthis
=undefined
!
Lazy lecturer
The remaining questions, QUESTION 2.a, 2.b, 3.a and 3.b, are trivial with my flow chart. So I will leave them as an exercise for you all.
Answers
https://repl.it/@ycmjason/What-is-this
Click run and you will see the answer in order (1.a, 1.b, 2.a, 2.b, 3.a, 3.b).
Note
- There is no "bound arrow function".
(() => {...}).bind(...)
is still the original arrow function. - For implicit calling, only the shape (
f()
orobj.f()
) matter. It doesn't matter wheref
comes from. Consider the following code:
const f = obj.f; // obj.f is a normal function
f(); // `this` in the body of `f` is `undefined`!!! not `obj`!!!
Updates:
- 16 July 2018: Thanks to @joshcheek for reminding me the correct binding of
this
of arrow functions!- 18 June 2018: Thanks to Yong for pointing out typo!