ES6: Arrows and Lexical This
Update: Async arrow function is fine! async () => { ... }
Arrow function is one of the highlights of ES6 that got many people excited (including me!). Lets look at its definition on MDN:
An arrow function expression (also known as fat arrow function) has a shorter syntax compared to function expressions and lexically binds the this value (does not bind its own this, arguments, super, or new.target). Arrow functions are always anonymous.
Overview on the syntax
Arrow functions are called arrow function because of the big fat =>
in its syntax. You can use them in place of anonymous functions.
// arrow function
(a, b, c) => {
return a + b — c;
}// is equivalent to
(function(a, b, c) {
return a + b — c;
}).bind(this);
There are some variations to its syntax. You can omit the braces if the function contains only single expression. It will return the result of the expression.
// without braces
(a, b, c) => a + b — c// is equivalent to
(a, b, c) => { return a + b — c; }// wrap with parenthesis to return object
(a, b, c) => ({a: a, b: b, c: c})
Then you can omit the parenthesis enclosing parameters if there is only one. However, the parenthesis is required if you have zero, two or more parameters. For consistency, most people suggest to always include parenthesis. This also makes adding/removing parameter later on easier.
// omit parenthesis
a => a + 1// is equivalent to
(a) => a + 1// but these cannot be omitted
() => { alert(‘hello’) }
(a, b) => a + b
We notice that the syntax of arrow function is indeed shorter. It saves us some keystrokes but whether it improves code readability is a matter of personal preference. I personally prefer arrow function whenever I need an anonymous function.
// cleaner function call
var numbers = [1, 2, 3, 4, 5, 6];
var mappedNumbers = numbers.map(n => n * 3);// versus
var mappedNumbers = numbers.map(function(n) {
return n * 3;
});
Is function still needed?
Yes. Arrow function does not replace the function keyword. function is still needed for declaration of named function, generators, async (es7), etc. However, you will see arrow functions are used in place of almost all anonymous functions in the future.
Lexical this
You might notice that arrow function is equivalent to (function(){…}).bind(this)
. The arrow function is bound to the current context. This is the real difference between arrow function and normal function expression. However, it does not matter if we are not using this in the function body.
To understand lexical this
, we need to understand lexical scope first. Todd Motto has written an excellent article on JavaScript scopes. I highly recommends everyone to have a read. In short, a lexical scope (or closure) is a scope that has access to the variables in its parent scope.
var foo = ‘bar’;
function parent() {
// `foo` is available here
console.log(‘Inside parent: ‘ + foo); function child() {
// `foo` is available here too
console.log(‘Inside child: ‘ + foo);
} child();
}parent(); // prints ‘Inside parent: bar’ and ‘Inside child: bar’
Lexical this extends this idea to the this
keyword.
function User() {
var _this = this;
setTimeout(() => {
console.log(_this === this);
}, 0);
}
var user = new User(); // prints `true`function User2() {
var _this = this;
setTimeout(function() {
console.log(_this === this);
}, 0);
}
var user2 = new User2(); // prints `false`
Lexical this
is easier to work with as it is defined by where the function is written, instead of the context it is being called.
One thing to note that arrow functions ignore any effort to change this
binding. Therefore, call, apply, bind and even the rules in strict mode cannot change the value of this
.
Miscellaneous
- IIFE form:
(() => {…})()
- Rest parameter can be used:
(…params) => {}
- [This is wrong. Async arrow function is fine]
async
does not work with arrow function because await is not allowed. If you see some code that uses them together, it will run into error - Chrome console supports arrow function already (I’m on Chrome 47)
Conclusion
Arrow function is a very welcomed feature in ES6. It provides a way to write function expression with shorter syntax. However, it is not intended to replace the function keyword completely. In addition to syntactic sugar, its lexical this
behaviour provides a solution to ES5 when dealing with this
. It is best to use arrow function in place of anonymous function.