We're looking for a user researcher to understand the needs of developers and designers. Is this you or someone you know? Check out the post: https://mzl.la/2IGzdXS

箭頭函式運算式(arrow function expression)擁有比函式運算式還簡短的語法。它沒有自己的 thisargumentssupernew.target 等語法。本函式運算式適用於非方法的函式,但不能被用作建構式(constructor)。

基本語法

(param1, param2, …, paramN) => { statements } (param1, param2, …, paramN) => expression
// 等於: => { return expression; }

// 只有一個參數時,括號才能不加:
(singleParam) => { statements }
singleParam => { statements }

//若無參數,就一定要加括號: () => { statements }

進階語法

// 用大括號將內容括起來,返回一個物件字面值表示法:
params => ({foo: bar}) 

// 支援其餘參數預設參數
(param1, param2, ...rest) => { statements } 
(param1 = defaultValue1, param2, …, paramN = defaultValueN) => { 
statements } 

// 也支援 parameter list 的解構
var f = ([a, b] = [1, 2], {x: c} = {x: a + b}) => a + b + c; f(); // 6

說明

也可參閱 "ES6 In Depth: Arrow functions" on hacks.mozilla.org

箭頭函式有兩個重要的特性:更短的函式寫法與 this 變數的非綁定。

更短的函式寫法

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

materials.map(function(material) { 
  return material.length; 
}); // [8, 6, 7, 9]

materials.map((material) => {
  return material.length;
}); // [8, 6, 7, 9]

materials.map(({length}) => length); // [8, 6, 7, 9]

this 不分家

在過去,函式的 this 變數在不同狀況下一直指向不同值(例如建構子呼叫時指向新物件、嚴格模式的 undefined、以物件方法呼叫時指向呼叫物件……),這樣的特性對物件導向程式設計來說其實相當麻煩。

function Person() {
  // 這個 Person() 建構式為它自己定義了 this
  this.age = 0;

  setInterval(function growUp() {
    // 在非嚴謹模式下, growUp() 函式把 this 定義為全域物件,
    // 而不是 Person() 建構式所定義的 this
    this.age++;
  }, 1000);
}

var p = new Person();

在 ECMAScript 3/5 裡面,有關 this 的問題,可以透過指派 this 值給可以關閉的變數解決。

function Person() {
  var self = this; // 有些人喜歡 `that` 而不是 `self`. 
                   // 選好一種取法後始終如一
  self.age = 0;

  setInterval(function growUp() {
    // 這個 callback 參考 `self` 變數,為預期中的物件。
    self.age++;
  }, 1000);
}

或者透過 bind 函式來綁定 this 變數到指定函式(以上面為例,就是 growUp() 函式)。

箭頭函式沒有它自己的 this:它會調用整個封閉的執行內文的 this 值,相當於將this當成closure中的變數。因此,以下程式碼內,傳遞給 setInterval 的 箭頭函式中的this ,會與封閉函式的 this 值相同:

function Person(){
  this.age = 0;

  setInterval(() => {
    this.age++; // |this| 適切的參考了Person建構式所建立的物件
  }, 1000);
}

var p = new Person();

和嚴格模式的關係

由於 this 變數具有詞彙上綁定意義,所以嚴格模式的宣告對 this 的作用將被忽略。

var f = () => {'use strict'; return this};
f() === window; // 或是 global 物件

但嚴格模式的其他作用依舊存在。

由 call 與 apply 函式呼叫

由於箭頭函式並沒有自己的 this,所以透過 call()apply() 呼叫箭頭函式只能傳入參數。thisArg 將會被忽略。

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));         // 顯示 2
console.log(adder.addThruCall(1)); // 依舊顯示 2

不綁定 arguments

箭頭函式並沒有自己的 arguments 物件。所以在此例中,arguments 只是參考到 enclosing 作用域裡面的相同變數:

var arguments = [1, 2, 3];
var arr = () => arguments[0];

arr(); // 1

function foo(n) {
  var f = () => arguments[0] + n; // foo's implicit arguments binding. arguments[0] is n
  return f();
}

foo(1); // 2

大多時候,使用其餘參數 是取代 arguments 物件的較好方式。

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

foo(1); // 11

將箭頭函式撰寫為方法

如同前述,箭頭函式運算式最適合用在非方法的函式。來看看如果要把它們當成方法來用,會發生什麼事:

'use strict';
var obj = {
  i: 10,
  b: () => console.log(this.i, this),
  c: function() {
    console.log(this.i, this);
  }
}
obj.b(); // 印出 undefined, Object {...}
obj.c(); // 印出 10, Object {...}

箭頭函式並沒有自己的 this。另一個例子與 Object.defineProperty() 有關:

'use strict';

var obj = {
  a: 10
};

Object.defineProperty(obj, 'b', {
  get: () => {
    console.log(this.a, typeof this.a, this); // undefined 'undefined' Window {...} (or the global object)
    return this.a + 10; // represents global object 'Window', therefore 'this.a' returns 'undefined'
  }
});

使用 new 運算子

箭頭函式不可作為建構式使用;若使用於建構式,會在使用 new 時候拋出錯誤。

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

使用 prototype 屬性

箭頭函式並沒有原型(prototype)屬性。

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

使用關鍵字 yield

yield 關鍵字無法用於箭頭函式的 body(except when permitted within functions further nested within it)。因此,箭頭函式無法使用 generator。

函式主體(Function body)

箭頭函式可以變成 concise body 或者平常使用的 block body。

在 concise body 裡面只需要輸入運算式,就會附上內建的回傳。在 block body 裡面就必須附上明確的 return 宣告。

var func = x => x * x;                  // concise 語法會內建 "return"
var func = (x, y) => { return x + y; }; // block body 需要明確的 "return"

回傳物件字面值

請注意只使用 params => {object:literal} 並不會按照期望般回傳物件字面值(object literal)。

var func = () => { foo: 1 };               // Calling func() returns undefined!
var func = () => { foo: function() {} };   // SyntaxError: Unexpected token (

因為在大括弧({})裡面的文字會被解析為有序宣告(例如 foo 會被當作標記(label)、而不是物件的 key )

要記得把物件字面值包在圓括弧內。

var func = () => ({foo: 1});
var func = () => ({ foo: function() {} }); 

換行

箭頭函式不可以在參數及箭頭間包含換行。

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

Parsing order

箭頭函式的箭頭儘管不是操作符,但藉著運算子優先等級,箭頭函式有著和普通函式不相同的特殊解析規則。

let callback;

callback = callback || function() {}; // ok
callback = callback || () => {};      // SyntaxError: invalid arrow-function arguments
callback = callback || (() => {});    // ok

更多範例

// 回傳 undefined 的箭頭函式
let empty = () => {};

(() => "foobar")() // 回傳 "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]

規範

規範 狀態 註解
ECMAScript 2015 (6th Edition, ECMA-262)
The definition of 'Arrow Function Definitions' in that specification.
Standard Initial definition.

瀏覽器相容性

FeatureChromeEdgeFirefoxInternet ExplorerOperaSafari
Basic support45 Yes221 2 No3210
Trailing comma in parameters58 ?52 No45 ?
FeatureAndroid webviewChrome for AndroidEdge mobileFirefox for AndroidOpera AndroidiOS SafariSamsung Internet
Basic support4545 Yes221 232105.0
Trailing comma in parameters5858 ?5245 ?7.0

1. 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.

2. 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.

參閱

文件標籤與貢獻者

最近更新: kuolun,