An arrow function expression has a shorter syntax compared to function expressions and does not bind its own this
, arguments, super, or new.target. Arrow functions are always anonymous. These function expressions are best suited for non-method functions and they can not be used as constructors.
Syntax
Basic Syntax
(param1, param2, …, paramN) => { statements } (param1, param2, …, paramN) => expression // equivalent to: => { return expression; } // Parentheses are optional when there's only one parameter: (singleParam) => { statements } singleParam => { statements } // A function with no parameters requires parentheses: () => { statements }
Advanced Syntax
// Parenthesize the body to return an object literal expression: params => ({foo: bar}) // Rest parameters and default parameters are supported (param1, param2, ...rest) => { statements } (param1 = defaultValue1, param2, …, paramN = defaultValueN) => { statements } // Destructuring within the parameter list is also supported var f = ([a, b] = [1, 2], {x: c} = {x: a + b}) => a + b + c; f(); // 6
Detailed syntax examples can be seen here.
Description
See also "ES6 In Depth: Arrow functions" on hacks.mozilla.org.
Two factors influenced the introduction of arrow functions: shorter functions and non-binding of this
.
Shorter functions
In some functional patterns, shorter functions are welcome. Compare:
var a = [ "Hydrogen", "Helium", "Lithium", "Beryllium" ]; var a2 = a.map(function(s){ return s.length }); var a3 = a.map( s => s.length );
No binding of this
Until arrow functions, every new function defined its own this
value (a new object in case of a constructor, undefined in strict mode function calls, the context object if the function is called as an "object method", etc.). This
proved to be annoying with an object-oriented style of programming.
function Person() { // The Person() constructor defines `this` as an instance of itself. this.age = 0; setInterval(function growUp() { // In non-strict mode, the growUp() function defines `this` // as the global object, which is different from the `this` // defined by the Person() constructor. this.age++; }, 1000); } var p = new Person();
In ECMAScript 3/5, this
issue was fixable by assigning the value in this
to a variable that could be closed over.
function Person() { var that = this; that.age = 0; setInterval(function growUp() { // The callback refers to the `that` variable of which // the value is the expected object. that.age++; }, 1000); }
Alternatively, a bound function could be created so that the proper this
value would be passed to the growUp()
function.
An arrow function does not create its own this
context, so this
has the original meaning from the enclosing context. Thus the following code works as expected.
function Person(){ this.age = 0; setInterval(() => { this.age++; // |this| properly refers to the person object }, 1000); } var p = new Person();
Relation with strict mode
Given that this
is lexical, strict mode rules with regard to this
are just ignored.
var f = () => {'use strict'; return this}; f() === window; // or the global object
The rest of strict mode rules apply normally.
Invoked through call or apply
Since this
is not bound in arrow functions, the call()
or apply()
methods can only pass in arguments; this
is ignored:
var adder = { base : 1, add : function(a) { var f = v => v + this.base; return f(a); }, addThruCall: function(a) { var f = v => v + this.base; var b = { base : 2 }; return f.call(b, a); } }; console.log(adder.add(1)); // This would log to 2 console.log(adder.addThruCall(1)); // This would log to 2 still
No binding of arguments
Arrow functions do not bind an arguments
object Thus, arguments
is simply a reference to the name in the enclosing scope.
var arguments = 42; var arr = () => arguments; arr(); // 42 function foo() { var f = (i) => arguments[0]+i; // foo's implicit arguments binding return f(2); } foo(1); // 3
Arrow functions don't have their own arguments
object, but in most cases rest parameters are a good alternative:
function foo() { var f = (...args) => args[0]; return f(2); } foo(1); // 2
Arrow functions used as methods
As stated, arrow function expressions are best suited for non-method functions. Let's see what happens when we try to use them as methods.
'use strict'; var obj = { i: 10, b: () => console.log(this.i, this), c: function() { console.log( this.i, this) } } obj.b(); // prints undefined, Window obj.c(); // prints 10, Object {...}
Arrow functions do not define ("bind") their own this
. Another example involving Object.defineProperty()
:
'use strict'; var obj = { a: 10 }; Object.defineProperty(obj, "b", { get: () => { console.log(this.a, typeof this.a, this); return this.a+10; // represents global object 'Window', therefore 'this.a' returns 'undefined' } });
Use of the new
operator
Arrow functions cannot be used as constructors and will throw an error when used with new
.
Use of the yield
keyword
The yield
keyword may not be used in an arrow function's body (except when permitted within functions further nested within it). As a consequence, arrow functions cannot be used as generators.
Function body
Arrow functions can have either a "concise body" or the usual "block body".
In a concise body, only an expression is needed, and an implicit return is attached. In a block body, you must use an explicit return
statement.
var func = x => x * x; // concise syntax, implied "return" var func = (x, y) => { return x + y; }; // with block body, explicit "return" needed
Returning object literals
Keep in mind that returning object literals using the concise syntax params => {object:literal}
will not work as expected:
var func = () => { foo: 1 }; // Calling func() returns undefined! var func = () => { foo: function() {} }; // SyntaxError: function statement requires a name
This is because the code inside braces ({}) is parsed as a sequence of statements (i.e. foo
is treated like a label, not a key in an object literal).
Remember to wrap the object literal in parentheses:
var func = () => ({ foo: 1 });
Newline
Arrow function cannot contain newline between parameter and arrow.
var func = () => 1; // SyntaxError: expected expression, got '=>'
Parsing order
The arrow in an arrow function is not an operator, but arrow functions have special parsing rules that interact differently with operator precedence, compared to a regular function.
let callback; callback = callback || function() {}; // ok callback = callback || () => {}; // SyntaxError: invalid arrow-function arguments callback = callback || (() => {}); // ok
More examples
// An empty arrow function returns undefined let empty = () => {}; (() => "foobar")() // IIFE, returns "foobar" var simple = a => a > 15 ? 15 : a; simple(16); // 15 simple(10); // 10 let max = (a, b) => a > b ? a : b; // Easy array filtering, mapping, ... var arr = [5, 6, 13, 0, 1, 18, 23]; var sum = arr.reduce((a, b) => a + b); // 66 var even = arr.filter(v => v % 2 == 0); // [6, 0, 18] var double = arr.map(v => v * 2); // [10, 12, 26, 0, 2, 36, 46] // More concise promise chains promise.then(a => { // ... }).then(b => { // ... });
Specifications
Specification | Status | Comment |
---|---|---|
ECMAScript 2015 (6th Edition, ECMA-262) The definition of 'Arrow Function Definitions' in that specification. |
Standard | Initial definition. |
ECMAScript 2017 Draft (ECMA-262) The definition of 'Arrow Function Definitions' in that specification. |
Draft |
Browser compatibility
Feature | Chrome | Firefox (Gecko) | Edge | IE | Opera | Safari |
---|---|---|---|---|---|---|
Basic support | 45.0 | 22.0 (22.0) | (Yes) |
No support |
32 | 10.0 |
Feature | Android | Android Webview | Firefox Mobile (Gecko) | IE Mobile | Opera Mobile | Safari Mobile | Chrome for Android |
---|---|---|---|---|---|---|---|
Basic support | No support | 45.0 | 22.0 (22.0) | No support | No support | 10.0 | 45.0 |
Firefox-specific notes
- The initial implementation of arrow functions in Firefox made them automatically strict. This has been changed as of Firefox 24. The use of
"use strict";
is now required. - Arrow functions are semantically different from the non-standard expression closures added in Firefox 3 (details: JavaScript 1.8), for expression closures do not bind
this
lexically. - Prior to Firefox 39, a line terminator (
\n
) was incorrectly allowed after arrow function arguments. This has been fixed to conform to the ES6 specification and code like() \n => {}
will now throw aSyntaxError
in this and later versions.