ECMAScript 2015 or ES 6 introduced arrow function expressions. It is a compact way of creating functions. Here is how you can create an arrow function.
(param1,param2, … paramN) => {
//statements
s}
As opposed to regular functions, they do not require a function keyword, and they are anonymous. Let’s take an example to see the difference between a regular and an arrow function expression.
var numbers = [1,2,3,4,5]
//regular function
es5_square = numbers.map(function(number){
return number*number
})
// arrow function
es6_square = numbers.map((number)=>{return number*number})
console.log(es5_square)
console.log(es6_square)
Output
(5) [1, 4, 9, 16, 25]
The above code calculates the square of each element of the array numbers. As you can observe, the arrow function is more concise.
Let's make it shorter.
If the arrow function contains a single parameter, then we can omit the parenthesis. Consider the same example.
var numbers = [1,2,3,4,5]
// arrow function
es6_square = numbers.map(number=>{return number*number})
console.log(es6_square)
Output
If the arrow function contains a single statement and that statement is a return statement, then you can omit the curly braces and the return
keyword. Look at the following example.
var numbers = [1,2,3,4,5]
// arrow function
es6_square = numbers.map(number=>number*number)
console.log(es6_square)
Output
However, if the arrow function contains zero or more than one parameter, then you have to include the parenthesis. Moreover, curly braces and return keyword are also necessary if the function has more than one statement.
var even = [2,4,6,8,10]
var odd = [1,3,5,7,9]
// sum of squares of corresponding even and odd numbers
es6_square = (a, b) => {
num1 = a*a
num2 = b*b
return num1 + num2
}
for(i=0;i<5;i++)
{
console.log(es6_square(even[i], odd[i]))
}
Output
25
61
113
181
Handling of this
The behavior of this
keyword is different in arrow and regular functions. In a regular function, this
keyword belongs to the function’s parent or the object on which the function was invoked. However, in arrow functions, this
value of the enclosing lexical scope is used. It refers to the current surrounding scope. Let’s take an example to understand this.
Consider the following example, where an object contains a property numbers
array, and the method test
displays it.
var obj = {
numbers : [1,2,3,4,5],
test : function(){
console.log(this.numbers)
}
}
obj.test()
Output
Since it is a regular function, this
refers to the object that called the method, i.e., obj.
Now, consider the following code.
var obj = {
numbers : [1,2,3,4,5],
test : function(){
this.numbers.forEach(function(number){
console.log(number + " belongs to the array " + this.numbers)
})
}
}
obj.test()
Output
2 belongs to the array undefined
3 belongs to the array undefined
4 belongs to the array undefined
5 belongs to the array undefined
Here, one would expect to get the same answer as in the previous example for this.numbers
. As we know, this
keyword belongs to the owner of the function. When this
keyword is inside the object’s method, it belongs to that object. However, when it is a stand-alone or inside another method, it becomes out of scope, and it belongs to the Window Object. Since the Window object does not have a numbers
property, it returns undefined.
Let’s discuss the two workarounds of this limitation in ES5. The first is to save the this value in a variable and use it where required. Look at the following code.
var obj = {
numbers : [1,2,3,4,5],
test : function(){
var self = this;
this.numbers.forEach(function(number){
console.log(number + " belongs to the array [" + self.numbers +"]")
})
}
}
obj.test()
Output
2 belongs to the array [1,2,3,4,5]
3 belongs to the array [1,2,3,4,5]
4 belongs to the array [1,2,3,4,5]
5 belongs to the array [1,2,3,4,5]
The second workaround is to use bind to attach this (that refers to the method’s object) to the function inside the object’s method.
var obj = {
numbers : [1,2,3,4,5],
test : function(){
this.numbers.forEach(function(number){
console.log(number + " belongs to the array [" + this.numbers +"]")
}.bind(this))
}
}
obj.test()
Output
2 belongs to the array [1,2,3,4,5]
3 belongs to the array [1,2,3,4,5]
4 belongs to the array [1,2,3,4,5]
5 belongs to the array [1,2,3,4,5]
Since these are just the workarounds and not the actual solution, arrow functions provide a simple and straightforward solution. Let’s see how.
var obj = {
numbers : [1,2,3,4,5],
test : function(){
this.numbers.forEach(number=>console.log(number + " belongs to the array [" + this.numbers +"]"))
}
}
obj.test()
Output
2 belongs to the array [1,2,3,4,5]
3 belongs to the array [1,2,3,4,5]
4 belongs to the array [1,2,3,4,5]
5 belongs to the array [1,2,3,4,5]
The above code gives us the required output because this
in arrow function uses lexical scoping. As arrow functions do not have their own this
value, they use it from the enclosing scope.
Call() and Apply()
Since an arrow function does not have its own this, call() and apply() do not work in the desired way. Consider the following example.
var obj2 = {
test : exponent => console.log(Math.pow(this.base, exponent) + " ")
}
var obj1 = {
base: 4
}
obj2.test.call(obj1, 2)
Output
Where should we use (not use) arrow functions?
As arrow function provides the shorthand notation, they are preferred over regular functions for callbacks in methods like forEach(), map(), etc. Arrow function expressions make the code more readable and concise. As we have already seen above, no binding of this keyword gives us many advantages. However, before using arrow functions, we need to think about the required behavior of this keyword.
Arrow functions are not preferred to be used in the object’s methods. Let’s see an example
var obj = {
number: 3,
test: () => {
this.number*this.number
}
}
obj.test()
console.log(obj.number)
Output
3
As we can see, we did not get the desired answer because of the different behavior of this
in arrow functions.
Arrow functions cannot be used as a constructor. Therefore, it will throw an error when we create objects using the new
operator.
var Person = name => this.name = name;
var p1 = new Person("xyz")
Output