三、ES6系列之箭头函数

ES6 增加了箭头函数,箭头函数表达式的语法比函数表达式更简洁,并且没有自己的 thisargumentssupernew.target。箭头函数表达式更适用于那些本来需要匿名函数的地方,并且它不能用作构造函数。

基础语法

1
2
3
4
5
6
7
var func = (value, label) => {
value, label;
};
// 等同于
var func = function (value, label) {
return { value, label };
};

高级语法

1
2
3
4
5
6
7
8
9
10
11
//加括号的函数体返回对象字面量表达式:
params => ({foo: bar})

//支持剩余参数和默认参数
(param1, param2, ...rest) => { statements }
(param1 = defaultValue1, param2, …, paramN = defaultValueN) => {
statements }

//同样支持参数列表解构
let f = ([a, b] = [1, 2], {x: c} = {x: a + b}) => a + b + c;
f(); // 6

没有 this

箭头函数没有 this,所以需要通过查找作用域链来确定 this 的值。
this 的绑定一直是一个比较头疼的问题,请看下面的列子:

1
2
3
4
5
6
7
8
9
var age = 0;
function Person() {
this.age = 0;
setTimeout(function ageUp() {
this.age++;
console.log(this); // window
}, 1000);
}
var p = new Person();

上面这个例子中 p.age 的值一直都是 0,增加的是全局中 age 的值,因为函数 ageUp 中的 this 指向全局 window ,在 ECMAScript 3/5 中,通过将 this 值分配给封闭的变量,可以解决 this 问题。

1
2
3
4
5
6
7
8
9
10
var age = 0;
function Person() {
var _this = this;
_this.age = 0;
setTimeout(function ageUp() {
_this.age++;
console.log(_this); // 实例p
}, 1000);
}
var p = new Person();

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

1
2
3
4
5
6
7
8
9
10
11
12
var age = 0;
function Person() {
this.age = 0;
setTimeout(
function ageUp() {
this.age++;
console.log(this); // 实例p
}.bind(this),
1000
);
}
var p = new Person();

箭头函数不会创建自己的 this,它只会从自己的作用域链的上一层继承 this。因此,在下面的代码中,传递给 setInterval 的函数内的 this 与封闭函数中的 this 值相同:

1
2
3
4
5
6
7
8
9
10
function Person() {
this.age = 0;

setInterval(() => {
this.age++; // |this| 正确地指向 p 实例
console.log(this); // 实例p
}, 1000);
}

var p = new Person();

最后,因为箭头函数没有 this,所以也不能用 call()、apply()、bind() 这些方法改变 this 的指向,可以看一个例子:

1
2
3
var value = 1;
var result = (() => this.value).bind({ value: 2 })();
console.log(result); // 1

没有 arguments

箭头函数没有自己的 arguments 对象,所以箭头函数可以访问外围函数的 arguments 对象:

1
2
3
4
5
6
function constant() {
return () => arguments[0];
}

var result = constant(1);
console.log(result()); // 1

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

1
let foo = (...args) => args;

不能使用 new 操作符

JavaScript 函数有两个内部方法:[[Call]] 和 [[Construct]]。

当通过 new 调用函数时,执行 [[Construct]] 方法,创建一个实例对象,然后再执行函数体,将 this 绑定到实例上。

当直接调用的时候,执行 [[Call]] 方法,直接执行函数体。
箭头函数并没有 [[Construct]] 方法,不能用作构造函数,和 new 一起用会抛出错误。

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

没有 prototype

箭头函数没有 prototype 属性。

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

没有 new.target

因为不能使用 new 调用,所以也没有 new.target 值。

没有 super

连原型都没有,自然也不能通过 super 来访问原型的属性,所以箭头函数也是没有 super 的,不过跟 this、arguments、new.target 一样,这些值由外围最近一层非箭头函数决定。

参考文献