Arrow function: What is this?
Promise), most of them are designed as the extensions to or the improvements of the existing syntaxes and features, making developers’ lives easier.
Among those new syntaxes, I’ve found the “arrow function” feature to be so useful and brilliant that I often find myself writing the good old
function keyword to start defining a function due to my muscle memory being activated, before replacing it with the little
=> symbol, allowing me to implement more succinct, readable code.
What is an arrow function?
=>), hence its name.
Notice how I’ve replaced the
function keyword with the
=> symbol, removed the curly braces, made the function one-liner, assigned the entire expression
() => console.log('foo'); to
foo, which is a “constant” (
const is also part of the ES6 specification and I encourage you to check the detailed explanation on the MDN documentation if you are unsure about how it works. Basically it serves the same role as the
var keyword, except that it’s scoped on a “block” level, and it cannot be reassigned nor redeclared. It also has to be initialized with a value when it’s declared).
The benefits of arrow functions will become apparent when they are used as a callback function as shown in the following example.
On the last line, note how I was able to omit the
return keyword. This is because the arrow function knows to return an expression that comes after
=> when expressed as a one-liner.
We can even remove the parentheses around the function argument if there is only 1 argument provided.
What is this?
With a brief introduction to the arrow function completed, let’s now look at the main topic of this article, which is to understand what
this refers to within an arrow function (yes, I used the title of this article to mean the
The reason we should care about the notion of
this is interpreted between the two notations. Namely,
this within an arrow function is defined with a “lexical scope” whereas
this within a “normal” function, by which I mean a function defined with the
functionkeyword, is defined with a “dynamic scope”.
Put more simply, an arrow function’s
this is determined already when the function is written while a normal function’s
this is determined only when the function is executed.
(for more precise explanations, you might want to check this SO post)
Let’s look at the following program to make this point clearer.
Here, we are trying to output persons’ names along with their random greetings by referencing the relevant method and object (i.e.
getRandomElement method and
greetings array) from within the callback function for the
forEach method. You may also notice how an object’s methods can be defined without the
function keyword, which is also part of the ES6 specification. This is effectively the same as the following code, albeit with less typing involved.
If we run
obj.normal() , the program will throw an error since the execution context (i.e.
this) of the callback function for
Window object. This is due to the callback function being “dynamically” scoped as mentioned previously.
this is set to
Window, which doesn’t have the
getRandomElement function (method) defined,
this.getRandomElement will return
undefined , resulting in the error because we are providing
undefined with parentheses and an argument as if we are trying to call a function, which it is not.
Before ES6, we had to get around this problem (or feature) of
this referencing an undesirable object by using one of the following “tricks”.
- Cache the context
thisinto a variable:
2. Use the
bind method (Note that
apply cannot be used here as they will actually invoke the function, rather than return it and provide the returned function as the
forEach method’s callback function, which can be achieved by
3. Supply additional argument as context (if available)
As a developper, we all wonder why we have to go through these kinds of laborious process for a simple, yet quite common, task (i.e. iterating over a collection and doing something for each item within the collection).
With an arrow function, you don’t have to worry about the execution context being changed when a function is executed. All we have to pay attention to is “how the code is written”, by which I simply mean that we can treat the callback function as if it’s being run with the same context as its surrounding code.
Being lexically scoped, an arrow function shares the scope of its parent function even after the parent function has returned. It therefore has access to
this defined on line 10 of the
arrow method body (i.e.
this.persons). Even if the function is executed later, its context remains the same as when it was defined. It doesn’t change as is the case for a dynamically defined function whose context depends uniquely upon how it’s invoked.
I hope the above example could give you a glimpse into the power of an arrow function. No longer will you find yourself becoming annoyed by mistyping the
function keyword and watching an error displayed on the console.
Everything in our life, though, seems to come with tradeoffs. And the expressive power of an arrow function is no exception.
From the next article, we will look at how an arrow function can play havoc with its sharp arrowhead (and what we can do to avoid it), starting from its behaviour within the
$.each , and
Any comment or suggestion on how the article can be improved will be greatly appreciated!