Arrow functions

Arrow functions

箭头函数表达式的语法比函数表达式更短,并且不绑定自己的this,arguments,super或 new.target。这些函数表达式最适合用于非方法函数,并且它们不能用作构造函数。

语法

基础语法

(param1, param2, …, paramN) => { statements } (param1, param2, …, paramN) => expression // equivalent to: (param1, param2, …, paramN) => { return expression; } // Parentheses are optional when there's only one parameter name: (singleParam) => { statements } singleParam => { statements } // A function with no parameters should be written with a pair of parentheses. () => { statements }

高级语法

// Parenthesize the body of function 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 let 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(material => material.length // [8, 6, 7, 9]

不绑定this

在箭头函数出现之前,每个新定义的函数都有它自己的 this值(在构造函数的情况下是一个新对象,在严格模式的函数调用中为 undefined,如果该函数被称为“对象方法”则为基础对象等)。This被证明是令人厌烦的面向对象风格的编程。

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(

在ECMAScript 3/5中,通过将this值分配给封闭的变量,可以解决this问题。

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 }

或者,可以创建绑定函数,以便将预先分配的this值传递到绑定的目标函数(上述示例中的growUp()函数)。

箭头函数不会创建自己的this;它使用封闭执行上下文的this值。因此,在下面的代码中,传递给setInterval的函数内的this与封闭函数中的this值相同:

function Person(){ this.age = 0; setInterval(() => { this.age++; // |this| properly refers to the person object }, 1000 } var p = new Person(

与严格模式的关系

鉴于 this 是词法层面上的,严格模式中与 this 相关的规则都将被忽略。

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

严格模式的其他规则依然不变.

通过 call 或 apply 调用

由于 this已经在词法层面完成了绑定,通过 call() 或apply() 方法调用一个函数时,只是传入了参数而已,对 this 并没有什么影响:

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

不绑定arguments

箭头函数不绑定Arguments 对象。因此,在本示例中,参数只是在封闭范围内引用相同的名称:

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

在大多数情况下,使用剩余参数是使用arguments对象的好选择。

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

像方法一样使用箭头函数

如上所述,箭头函数表达式对非方法函数是最合适的。让我们看看当我们试着把它们作为方法时发生了什么。

'use strict'; var obj = { i: 10, b: () => console.log(this.i, this), c: function() { console.log(this.i, this } } obj.b( // prints undefined, Window {...} (or the global object) obj.c( // prints 10, Object {...}

箭头函数没有定义this绑定。另一个涉及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' } }

使用 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 关键字通常不能在箭头函数中使用(除非是嵌套在允许使用的函数内)。因此,箭头函数不能用作生成器。

函数体

箭头函数可以有一个“简写体”或常见的“块体”。

在一个简写体中,只需要一个表达式,并附加一个隐式的返回值。在块体中,必须使用明确的return语句。

var func = x => x * x; // concise syntax, implied "return" var func = (x, y) => { return x + y; }; // with block body, explicit "return" needed

返回对象字面量

记住用params => {object:literal}这种简单的语法返回对象字面量是行不通的。

var func = () => { foo: 1 }; // Calling func() returns undefined! var func = () => { foo: function() {} }; // SyntaxError: function statement requires a name

这是因为花括号({})里面的代码被解析为一系列语句(即 foo 被认为是一个标签,而非对象字面量的组成部分)。

所以,记得用圆括号把对象字面量包起来:

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

换行

箭头函数在参数和箭头之间不能换行。

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

解析顺序

虽然箭头函数中的箭头不是运算符,但箭头函数具有与常规函数不同的特殊运算符优先级解析规则。

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

更多示例

// An empty arrow function returns undefined let empty = () => {}; (() => 'foobar')( // Returns "foobar" // (this is an Immediately Invoked Function Expression // see 'IIFE' in glossary) 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

规范

SpecificationStatusComment
ECMAScript 2015 (6th Edition, ECMA-262)The definition of 'Arrow Function Definitions' in that specification.StandardInitial definition.
ECMAScript Latest Draft (ECMA-262)The definition of 'Arrow Function Definitions' in that specification.Living Standard

浏览器兼容性

FeatureChromeEdgeFirefox (Gecko)Internet ExplorerOperaSafari
Basic support45.0(Yes)22.0 (22.0)No support3210.0
Trailing comma in parameters??52.0 (52.0)???

FeatureAndroidAndroid WebviewEdgeFirefox Mobile (Gecko)IE MobileOpera MobileSafari MobileChrome for Android
Basic supportNo support45.0(Yes)22.0 (22.0)No supportNo support10.045.0
Trailing comma in parameters???52.0 (52.0)????

Firefox特定的笔记

  • Firefox中的箭头函数的初始实现使得它们自动严格。这已经改变为Firefox 24。采用"use strict";它现在需要的。

  • 箭头函数在语义上与Firefox 3中添加的非标准表达式闭包不同(细节:JavaScript 1.8),是由于表达式闭包不会在this词法上绑定。

  • 在Firefox 39之前,在箭头函数参数之后不正确地允许行结束符(\n)。这已经被修正,以符合ES2015规范和像() \n => {}现在将在这个和以后的版本的代码中抛出一个SyntaxError。