Promise), most of them will serve as the extensions to or the improvements of the existing syntaxes and features to make developers’ lives easier.
Among those new syntaxes, I’ve found the “arrow function” feature so useful 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, which allows me to implement more succinct, readable code.
What is an arrow function?
=>symbol serving as a syntactic sugar for easily defining the function, hence its name. You can easily define an arrow function:
Notice how I’ve replaced the
function keyword with the
=> symbol, removed the curly braces, and assigned the entire expression
() => console.log('foo'); to
foo, which is a “constant” (
const is also part of the ES6 specification. You can check the MDN documentation for more information. 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 the brief introduction to 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 in an arrow function is interpreted differently than in a “normal” function. Namely,
this within an arrow function is defined with a “lexical scope” whereas
this within a “normal” function, or a function defined with the
functionkeyword, is defined with a “dynamic scope”.
Put more simply, an arrow function’s
this has already been determined when the function is written while a “normal” function’s
this will be 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 people’s names along with their random greetings by referencing the relevant method and object (i.e. the
getRandomElement method and the
greetings array) from within the callback function for the
forEach method. You may also notice how the 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, 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
undefined 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 we can achieve with
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 be aware of is “how the code is written”; the callback function will be 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 on line 10 of the
arrow method body (i.e.
this.people). Even if the callback function is executed later, its context remains the same as when it was defined. It doesn’t change as in 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!