MDN’s new design is in Beta! A sneak peek: https://blog.mozilla.org/opendesign/mdns-new-design-beta/

Hàm rút gọn

This translation is incomplete. Please help translate this article from English.

Hàm biểu thức rút gọn có cú pháp ngắn gọn hơn hàm biểu thức và không phụ thuộc vào chính nó thisargumentssuper, or new.target. Có nhiều hàm biểu thức áp dụng tốt cho hàm vô danh ( không có tên phương thức), nhưng chúng lại không thể sử dụng như một hàm khởi tạo.

Cú pháp

Cú pháp cơ bản

(param1, param2, …, paramN) => { statements }
(param1, param2, …, paramN) => expression
// tương đương với: (param1, param2, …, paramN) => { return expression; }

// Dấu ngoặc đơn không bắt buộc khi chỉ có một tham số truyền vào:
(singleParam) => { statements }
singleParam => { statements }

// Hàm khi không có tham số truyền vào bắt buộc phải là dấu ():
() => { statements }
() => expression // tương đương: () => { return expression; }

Cú pháp nâng cao

// Bên trong dấu ngoặc đơn là một đối tượng:
params => ({foo: bar})

// Rest parameters và default parameters được hỗ trợ
(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

Chi tiết các ví dụ bạn có thể xem ở đây.

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.

Hàm ngắn

Một vài ví dụ, cú pháp hàm rút gọn luôn được coder yêu thích, cùng so sánh:

var materials = [
  'Hydrogen',
  'Helium',
  'Lithium',
  'Beryllium'
];

// thông thường
var materialsLength1 = materials.map(function(material) { 
  return material.length;
});

// ngắn hơn (như mùa đông 5 độ vậy)
var materialsLength2 = materials.map((material) => {
  return material.length;
});

// ngắn hơn nữa (và -2 độ, bạn còn bao nhiêu cm ?)
var materialsLength3 = materials.map(material => material.length);

 

Không ràng buộc this

Until arrow functions, every new function defined its own this value (a new object in the 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, the 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 bound target function (the growUp() function in the example above).

An arrow function does not create its own this context, so this has its 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 ignored.

var f = () => { 'use strict'; return this; };
f() === window; // or the global object

All other strict mode rules apply normally.

Invoked through call or apply

Since this is not bound in arrow functions, the methods call() or apply() can only pass in parameters. 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, in this example, arguments is simply a reference to the same 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

In most cases, using rest parameters is a good alternative to using an arguments object.

function foo() { 
  var f = (...args) => args[0]; 
  return f(2); 
}

foo(1); // 2

Arrow functions used as methods

As stated previously, 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, Object {...}
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.

var Foo = () => {};
var foo = new Foo(); // TypeError: Foo is not a constructor

Use of prototype property

Arrow functions do not have a prototype property.

var Foo = () => {};
console.log(Foo.prototype); // undefined

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});

Line breaks

An arrow function cannot contain a line break between its parameters and its arrow.

var func = ()
           => 1; // SyntaxError: expected expression, got '=>'

Parsing order

Although the arrow in an arrow function is not an operator, arrow functions have special parsing rules that interact differently with operator precedence compared to regular functions.

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 => {
   // ...
});

// Parameterless arrow functions that are visually easier to parse
setTimeout( () => {
  console.log('I happen sooner');
  setTimeout( () => {
    // deeper code
    console.log('I happen later');
  }, 1);
}, 1);  

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
Trailing comma in parameters ? 52.0 (52.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
Trailing comma in parameters ? ? 52.0 (52.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 ES2015 specification and code like () \n => {} will now throw a SyntaxError in this and later versions.

See also

Document Tags and Contributors

 Contributors to this page: anhnc
 Last updated by: anhnc,