箭头函数


ES6标准新增了一种新的函数:Arrow Function(箭头函数)。

为什么叫Arrow Function?因为它的定义用的就是一个箭头:



x => x * x


上面的箭头函数相当于:



function (x) {
return x * x;
}


箭头函数相当于匿名函数,并且简化了函数定义。箭头函数有两种格式,一种像上面的,只包含一个表达式,连​​{ ... }​​和​​return​​都省略掉了。还有一种可以包含多条语句,这时候就不能省略​​{ ... }​​和​​return​​:



ES6 箭头函数 =>_函数体

x => {
if (x > 0) {
return x * x;
}
else {
return - x * x;
}
}


ES6 箭头函数 =>_函数体


如果参数不是一个,就需要用括号​​()​​括起来:



ES6 箭头函数 =>_函数体

// 两个参数:
(x, y) => x * x + y * y

// 无参数:
() => 3.14

// 可变参数:
(x, y, ...rest) => {
var i, sum = x + y;
for (i=0; i<rest.length; i++) {
sum += rest[i];
}
return sum;
}


ES6 箭头函数 =>_函数体


如果要返回一个对象,就要注意,如果是单表达式,这么写的话会报错:



// SyntaxError:
x => { foo: x }


因为和函数体的​​{ ... }​​有语法冲突,所以要改为:



// ok:
x => ({ foo: x })


this

箭头函数看上去是匿名函数的一种简写,但实际上,箭头函数和匿名函数有个明显的区别:箭头函数内部的​​this​​是词法作用域,由上下文确定。

回顾前面的例子,由于JavaScript函数对​​this​​绑定的错误处理,下面的例子无法得到预期结果:



ES6 箭头函数 =>_函数体

var obj = {
birth: 1990,
getAge: function () {
var b = this.birth; // 1990
var fn = function () {
return new Date().getFullYear() - this.birth; // this指向window或undefined
};
return fn();
}
};


ES6 箭头函数 =>_函数体


现在,箭头函数完全修复了​​this​​的指向,​​this​​总是指向词法作用域,也就是外层调用者​​obj​​:



ES6 箭头函数 =>_函数体

var obj = {
birth: 1990,
getAge: function () {
var b = this.birth; // 1990
var fn = () => new Date().getFullYear() - this.birth; // this指向obj对象
return fn();
}
};
obj.getAge(); // 25


ES6 箭头函数 =>_函数体


如果使用箭头函数,以前的那种hack写法:



var that = this;


就不再需要了。

由于​​this​​在箭头函数中已经按照词法作用域绑定了,所以,用​​call()​​或者​​apply()​​调用箭头函数时,无法对​​this​​进行绑定,即传入的第一个参数被忽略:



ES6 箭头函数 =>_函数体

var obj = {
birth: 1990,
getAge: function (year) {
var b = this.birth; // 1990
var fn = (y) => y - this.birth; // this.birth仍是1990
return fn.call({birth:2000}, year);
}
};
obj.getAge(2015); // 25


ES6 箭头函数 =>_函数体


 

 

使用注意点

箭头函数有几个使用注意点。

(1)函数体内的​​this​​对象,就是定义时所在的对象,而不是使用时所在的对象。

(2)不可以当作构造函数,也就是说,不可以使用​​new​​命令,否则会抛出一个错误。

(3)不可以使用​​arguments​​对象,该对象在函数体内不存在。如果要用,可以用 rest 参数代替。

(4)不可以使用​​yield​​命令,因此箭头函数不能用作 Generator 函数。



ES6 箭头函数 =>_函数体

function Timer() {
this.s1 = 0;
this.s2 = 0;
// 箭头函数
setInterval(() => this.s1++, 1000);
// 普通函数
setInterval(function () {
this.s2++;
}, 1000);
}

var timer = new Timer();

setTimeout(() => console.log('s1: ', timer.s1), 3100);
setTimeout(() => console.log('s2: ', timer.s2), 3100);
// s1: 3
// s2: 0


ES6 箭头函数 =>_函数体


上面代码中,​​Timer​​函数内部设置了两个定时器,分别使用了箭头函数和普通函数。前者的​​this​​绑定定义时所在的作用域(即​​Timer​​函数)

,后者的​​this​​指向运行时所在的作用域(即全局对象)。所以,3100毫秒之后,​​timer.s1​​被更新了3次,而​​timer.s2​​一次都没更新。