Note: This post is work-in-progress learning-note and still in active development and updated regularly.
JavaScript (JS) functions are the building blocks of the program, as they allow use perform an operation many times by calling name of code block without repetition. In the previous posts, we discussed Understanding JavaScript Functions – The Basics, Function Declaration & Expression, Callback functions in detail.
In ECMAScript 2015 (ES6) standard added new features to JS functions and introduced Arrow functions expression with shorter syntax compared to function expression.
In this learning-note post, introduction to arrow function, converting an function to arrow function, its basic syntax, some use case examples will be discussed.
Arrow Functions
The Arrow functions are similar to anonymous function expression and defined with a new syntax that uses a fat “arrow” (=>
). Arrow functions make code much simpler and shorter.
//initialize a sum function with two arg
let sum = function(a,b) {
return a + b;
}
//access sum()
console.log(sum(15,25));//OUTPUT => 40
In the example above, the sum()
, a function expression, is defined with with two arguments (line 2) and it returns sum of two numbers (line 6).
This type of functions are also known as anonymous functions (functions without name). Functions stored in a variables, such as expression function, do not need function name and can be invoked (accessed) using variable name.
In ECMAScript 6, this above sum function expression can also be written using arrow function as shown below:
//using arrow function syntax
let sum = (a,b) => a + b;
console.log(sum(15,25)); //OUTPUT => 40
//check typeof()
console.log(typeof sum); // function
//check instanceof
console.log(sum instanceof Function); // true
Using the sum
arrow function is returns exactly same value (line 9) like in previous example (line 6).
The above sum
arrow function, the typeof
operator returns functions (line 12) indicating it’s a function. Like wise, the sum
arrow function returns instanceof
function type as true
(line 14).
Writing Arrow Functions
An anonymous function, from above example (lines: 2-4) was converted to an arrow function (line 8) as follows:
- First, delete the
function
keyword - Add a flat arrow like this
=>
- Remove parentheses from single parameters
- No need to use
return
keyword in the body code block
Arrow functions are always anonymous, the name is an empty string and argument object is not available.
Arrow Function Syntax
The basic syntax of arrow functions is variable depending upon the parameters (or arguments) used. Basic syntax examples (from MDN)
//syntax with parameters
() => { ... } // with no parameter
x => { ... } // with one parameter
(x, y) => { ... } // with several parameters
- The parameter list for a function with no parameters should be written with a pair of parentheses (line 2).
- Parentheses are optional when there is only one parameter (line 3).
- With multiple parameters, the parameters are separated by comma and must be enclosed in parentheses (line 4).
With Multiple Parameters
//ES5 syntax
let funcName = function(arg1, arg2, ...argN) {
return expression;
}
//ES6 syntax
let funcName = (arg1, arg2, ...argN) => expression
//EXAMPLE with three arguments
//ES5 syntax
let add = function(num1, num2, num3) {
return num1 + num2 + num3;
};
//ES6 syntax
let add =(num1, num2, num3) => num1 + num2 + num3;
In cases where there are more than one arguments, then they must be enclosed in parentheses (line: 6, 14 ). The add()
function adds the two arguments (num1
, num2
) together and returns the results. Unlike in single arguments, the arguments are enclosed in parentheses and separated by commas (lines: 6, 14).
Another example with four parameters:
//ES5 syntax
let num = [9,12,6,32];
num.sort(function(x,y){
return y - x;
});
console.log(num); // OUTPUT => [ 32, 12, 9, 6 ]
//ES6 syntax
let num = [9,12,6,32];
num.sort((x,y) => y - x);
console.log(num); // OUTPUT => [ 32, 12, 9, 6 ]
In the example, the ES6 syntax is shorter and with the same results (line: 6 & 11).
With Single Parameters
With single parameter, use of parentheses are optional as shown below:
//single param basic syntax
(singleParam) => { statements }
singleParam => { statements }
// use case example
//ES5 syntax
let sum = function(x) { return x + 5 };
//ES6 syntax (can be written as)
let sum = x => x + 5;
Parenthesis are optional when there is only one parameter.
// SINGLE Parameter
//ES6 syntax
let cars = ['Toyota', 'Honda', 'BMW', 'Lexus'];
let stringLength = cars.map(car => car.length);
console.log(stringLength); // OUTPUT => [ 6, 5, 3, 5 ]
With No Parameters
The parameter list for a function with no parameters should be written with a pair of parentheses.
//basic syntax - with no argument
() => { statements }
//ES5 use case
let myCar = function car() {
console.log('Toyota');
};
//access myCar()
myCar()//OUTPUT => Toyota
//ES6 use case
let myCar = () => { console.log('Toyota'); };
//access myCar()
myCar(); //OUTPUT => Toyota
Parenthesis is required if there are no parameters.
Object Literals Syntax
Because both the object and body block of a function use curly brackets { }
, JS engine can’t distinguish between an object or block of codes.
// ES5 function expression
let myCar = function (make) {
return {value: make}
};
//
let carMake = myCar('Toyota');
console.log(carMake.value); //OUTPUT => Toyota
//with arrow function
let myCar = make => {value: make }; //logs undefined
//assign
let carMake = myCar('Toyota');
console.log(carMake.make);
//OUTPUT
Uncaught TypeError: Cannot read property 'make' of undefined
In the example above using ES5 function expression, myCar()
function returns an object that has value property set to make
argument (line 6). The same myCar()
function with ES6 arrow function syntax (line 10) with object literal returns error (line 15). This is because, both the function code block and object literal use curly brackets { }
and JS engine can’t distinguish them.
To fix this problem, the object literals codes should be enclosed in parentheses ( )
as shown below (line 17) which returns expected output (line 20). This is same result as in line 7.
//ES6 syntax with enclose brackets
let myCar = make => ({value: make}); //logs undefined
//function call
let carMake = myCar('Toyota');
console.log(carMake.value); //OUTPUT => Toyota
As shown above, the object literal must be enclosed in parentheses (line 17).
Line Breaks
Arrow functions syntax does not allow line break between the parameter definition and its fat arrow ( =>
) as shown below:
//incorrect - throws error
let sum = (a,b)
=> a + b;
//OUTPUT
SyntaxError: Unexpected token =>
//correct syntax
let sum = (x,y) =>
x + y; //OUTPUT => undefined
//this is also correct
let sum = (
x,
y
) =>
x + y; //OUTPUT => undefined
As shown above, line break between parameters (lines: 8-9) and lines (12-16) is allowed.
Arrow Function Body
Arrow function syntax can be written with concise body or block body as shown below:
// concise body syntax, implied "return"
var sum = a => a + 5;
// with block body, explicit "return" needed
var sum = (a, b) => { return a + b; };
Writing arrow function syntax with concise body syntax, expression is specified that serves as its return
value (line 2). If block body is in arrow function, curly brackets { }
and explicit return
keyword is required (line 5).
Arrow Function Features
Arrow function have two key features: shorter functions and no separate this
keyword.
Shorter Syntax
One of the major reasons that the arrow function is most popular ES6 feature is because of its shorter syntax. Using example from the MDN:
// function expression
let vehicles = [
'BMW',
'Lexus',
'Honda',
'Toyota'
];
//ES5 syntax
vehicles.map(function(vehicle ) {
return vehicle.length;
}); //OUTPUT => [3, 5, 5, 6]
//ES6 arrow function syntax
vehicles.map(vehicle => {
return vehicle.length;
}); //OUTPUT => [3, 5, 5, 6]
// alternative
vehicles.map(vehicle => vehicle.length); // => [3, 5, 5, 6]
// alternative
vehicles.map(({ length }) => length); // => [3, 5, 5, 6]
The example snippets above demonstrates that ES6 arrow function syntax allows to write function expression (lines: 2-11) to much shorter (lines: 14-16) and even in single line (line: 18, 20) with the same output.
No Separate this
Binding
The use of <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/this">this</a>
keyword in JS confusing. Its value varies depending upon the execution context. In arrow functions, this
retains the value of the enclosing lexical context‘s this
. Lets review context and use of this
in classic function first.
// function expression
let vehicle = {
make: 'BMW',
myCar: function() {
console.log(this.make)
}
}
//access
vehicle.myCar(); //OUTPUT => BMW
In the example above, vehicle
is an object which is calling myCar
function and it’s myCar’s context and the value of myCar
function is bound to the vehicle object.
//define vehiv=cle object
let vehicle = {
make: 'BMW',
// define myCar function expression
myCar: function() {
console.log(this.make) // outputs - BMW
// autonomous function
setTimeout(function() {
console.log(this.make) //outputs - undefined
}, 1000)
}
}
//access
vehicle.myCar() //OUTPUT => BMW .. then undefined
In the example above, value of this in myCar()
refers to vehicle
object loging myCar.make
within the function (line 5) correctly prints BMW
in the output (line 6). When a second function setTimeout
is added (lines: 8-10), the context of this.make
(line 9) is different here because this
here refers to window
object in the browser (instead of vehicle
object we might have thought it meant to be). That is why it outputs undefined
(line 14).
This can be fixed by assigning this to a variable which is usually named as self
or that
, which is in lexical scope of the callback function.
To fix this, you often assign the this
to a variable that doesn’t shadow inside the anonymous callback function.
// create vehicle object
let vehicle = {
make: 'BMW',
//create myCar function
myCar: function() {
let self = this; // assign to a var
console.log(this.make)
//autonomous function
setTimeout(function() {
console.log(self.make) //new variable
}, 1000)
}
}
//access
vehicle.myCar(); //OUTPUT => BMW ..then -- BMW
In the example above, this.make
(line 10) in callback refers to the self
variable of which the value is expected vehicle
object.
With use of arrow function (which does not have its own this
), this
uses the value of enclosing lexical context (surrounding block).
//create vehicle object
let vehicle = {
make: 'BMW',
myCar: function() {
console.log(this.make)
//arrow function
setTimeout(() => {
console.log(this.make)
}, 1000)
}
}
//access
vehicle.myCar(); //OUTPUT => BMW .. then ..MBW
Arrow functions take their value of this
from the lexical scope (meaning from its surrounding block). Arrow function captures this
from its surrounding context instead of creating its own context. In other words, it refers where it is defined.
Other Features
<strong>Strict mode</strong>
rules ignored: Becausethis
refers to surrounding global (lexical context), thestrict mode
rules with regard to thethis
keyword are ignored as shown below (example from the MDN):let myFunc = () => { 'use strict'; return this; }; //access myFunc myFunc() === window; //OUTPUT true //global object
In the example above,
this
(line 3) understrict mode
rules is ignored as shown in the outputtrue
(line 8).- Use of
new
keyword. Arrow function can’t be used as new constructor object, if used throws an error as shown below://arrow function let myFunc = () => {}; //create new constructor instance let myFunc = new myFunc(); // OUTPUT TypeError: Foo is not a constructor
- Use of
prototype
property: Arrow functions don’t have their ownprototype
property (logs –undefined
).//arrow function let myFunc = () => {}; console.log(myFunc.prototype); // OUTPUT => undefined
- Not suitable to use
Object.defineProperty()
: Arrow functions don’t have their own this, and suitable to use inObject.defineProperty()
as shown below (adopted from the MDN):'use strict'; //create let vehicle = { year: 2018 }; Object.defineProperty(vehicle, 'b', { get: () => { console.log(this.year, typeof this.year, this); return this.year + 10; } }); //access output console.log(vehicle.b); //OUTPUT undefined "undefined" Window {…} // global object NaN //represents window global object
In the example above,
this
in (line 9) refers to globalwindow
object outputsundefined
(line 16). Likewise,this
in (line 10) refers again to globalwindow
object thusthis.year
returnsundefined
(line 18). - No binding of
arguments
: Arrow functions don’t have their ownargument
object and acts simply a reference toargument
of adjacent scope, as demonstrated in the following example from the MDN:let arguments = [1, 2, 3]; let arr = () => arguments[0]; //access arr() arr(); // OUTPUT => 1 //create a vehicles() with one argument function vehicles(x) { let myCar = () => arguments[0] + x; return myCar(); } //access vehicles() vehicles(3); //OUTPUT => 6
In the example above, the
arguments[0]
in line 2 refers to arguments1
fromarguments
variable (line 1). When accessarr()
function output1
(line 4).Likewise the
arguments[0]
inmyCar()
function (line 10) refers to argumentx
ofvehicles()
function. Whenvehicles()
function is accessed with a argument value of3
, it returns an expected value of6
(line 12).
- Not suited as Methods: Because arrow function don’t have their own
this
keyword, they are not suitable to use in non-method function. An example of its use as method is presented in another post.
Functions vs Arrow Functions
Arrow functions behave differently than than the normal function. Some of the differences, as outlined by Nicolas Zakas & Kyle Pennell, are listed below:
- Use of
this
keyword: Thethis
keyword works differently in arrow function. The value ofthis
can’t be changed, which remains same entire the function life cycle. - Constructors: There is no built-in internal
[[constructor]]
method and can’t be used to create similar objects as in normal functions. Arrow function throws an error if used withnew
constructor. - No Prototype Property: Because new constructor can’t be used in arrow function, there is no prototype property.
- No Argument Object: Unlike function, arrow function don’t have local variable arguments. The MDN defines
arguments
object as ” anArray
-like object corresponding to the arguments passed to a function“.
Use Case Examples
Easier Array Mapping
Because the arrow function syntax is concise and commonly used to map
an array as shown in the following example.
//create & initialize arrays
const myVehicles = [
{ make:'Toyota', price:'20K'},
{ make:'BMW', price:'28K' },
{ make:'Honda', price:'18K' }
];
// ES5
let prices = myVehicles.map(function(myVehicles) {
return myVehicles.price;
})
console.log(prices); //OUTPUT => ["20K", "28K", "18K"]
//ES6 syntax with arrow function
const prices = myVehicles.map(myVehicles => myVehicles.price);
console.log(prices); //OUTPUT => ["20K", "28K", "18K"]
Using arrow function syntax, array map
syntax (lines: 8-10) can be written in a in single line (line 14).
Another example using array filter
method:
//create & initialise an array
const array = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15];
// ES5 - array filter divisible by 5
let newArray = array.filter(function (divFive){
return divFive % 5 === 0;
});
console.log(newArray); //OUTPUT => [5, 10, 15]
//ES6 - with arrow function syntax
let newArray = array.filter(divFive => divFive % 5 === 0);
console.log(newArray); //OUTPUT => [5, 10, 15]
The array syntax are also commonly used in functions that accept callback
like sort()
, map()
, reduce()
, filter()
to write simple code
Creating Immediate Invoking Function expression (IIFE)
The arrow functions are also commonly used in IIFE to create a closure()
function as shown below.
// IIFE syntax
let car = function(make) {
return {
myCar: function() {
return make;
}
};
}("Toyota");
//
console.log(car.myCar()); //OUTPUT => Toyota
//ES6 - IIFE with arrow function
let car = ((make) => {
return {
myCar: function() {
return make;
}
};
})("Toyota");
//
console.log(car.myCar()); //OUTPUT => Toyota
In the example above (adopted from Nicolas Zakas) to create an myCar()
method (lines: 2-8) using IIFE which uses an make
argument as return value (a private object). The same function can be written with arrow function (lines: 13-19) by wrapping the arrow function in parentheses ( )
, starting at (make)
(line 13) and closing after curly bracket }
(line 19).
Writing More Concise Promise Chains
JS promise and the then()
method will be discussed in detail separately. The following example from the MDN demonstrate use of arrow function to write more concise promise chains:
var p1 = new Promise( (resolve, reject) => {
resolve('Success!');
// or
// reject ("Error!");
} );
p1.then( value => {
console.log(value); // Success!
}, reason => {
console.log(reason); // Error!
} );
The following example example from Kyle Pennell post demonstrate use of allow function asynchronous callbacks function.
// ES5
aAsync().then(function() {
returnbAsync();
}).then(function() {
returncAsync();
}).done(function() {
finish();
});
//ES6 syntax with arrow function
aAsync().then(() => bAsync()).then(() => cAsync()).done(() => finish);
The ES6 function syntax (lines: 2-7) can be written in a single line (line 11) and much easier to read.
Easier Parsing Order
The following Arrow function (without parameters) example (adopted from the MDN) showing easier visual parsing:
// using timtiming event
setTimeout( () => {
console.log('I happen sooner');
setTimeout( () => {
// deeper code
console.log('I happen later');
}, 1); // unit - ms (millisecond)
}, 1);
//OUTPUT
I happen sooner
I happen later
The setTimeout()
method is the function to be executed and the second parameter refers to the number of milliseconds before execution.
The setTimeout()
and setInterval()
are both methods of the HTML DOM Window object.
Where Not to Use Arrow Functions
Arrow function can’t replace the classic function and its use is limited. Arrow Function is commonly used in (a) callback function, (b) should be defined before using them, and (c) can’t be used as methods or constructors. Some highlights arrow function pitfalls are discussed in a separate post.
Tip: Arrow functions are best suited for callbacks, methods like map, forEach
or reduce()
. The classic function expression are best suited for object methods.
Wrapping Up
In this learning-post tutorial, arrow function definition, writing arrow functions, arrow function syntax with no or multiple parameters and some use case examples were discussed. Arrow function is one the most popular ES6 features, its use is very diverse. For more detail discussion on the arrow function can be found in the links listed below.
Since arrow functions is not suitable to use in some cases, highlights of some some arrow function pitfall is discussed in a separate post.
Related Post: Some Pitfalls of Arrow Functions
Useful Resources & Links
While preparing this post, I have referred the following references extensively. Visit original link for additional information.
- Arrow Functions – MDN JavaScript Reference
- Understanding ECMAScript 6 : Arrow Functions | Nicholas C. Zakas
- Learning ES6: Arrow Functions – by Ben Ilegbodu | Eventbrite
- ES6 Arrow Functions: Fat and Concise Syntax in JavaScript | SitePoint
- JavaScript Arrow Functions Introduction | Wes Bos
- 6 ways to declare JavaScript functions |