=>到底是什么?我们今天就来一探究竟。
首先,我们谈论一些有关函数的事情。
函数表达式
JavaScript中有一个有趣的特性,无论何时,当你需要一个函数时,你都可以在想添加的地方输入这个函数。
举个例子,假设你尝试告诉浏览器用户点击一个特定按钮后的行为,你会这样写:
$("#save").click()
jQuery的.click()方法接受一个参数:一个函数。没问题,你可以在这里输入一个函数:
$("#save").click(function(){
requestData();
responseData();
})
对于现在的我们来说,写出这样的代码相当自然,而回忆起在这种编程方式流行之前,这种写法相对陌生一些,许多语言中都没有这种特性。1958年,Lisp首 先支持函数表达式,也支持调用lambda函数,而C++,Python、C#以及Java在随后的多年中一直不支持这样的特性。
现在截然不同,所有的四种语言都已支持lambda函数,更新出现的语言普遍都支持内建的lambda函数。我们必须要感谢JavaScript和早期的JavaScript程序员,他们勇敢地构建了重度依赖lambda函数的库,让这种特性被广泛接受。
令人伤感的是,随后在所有我提及的语言中,只有JavaScript的lambda的语法最终变得冗长乏味。
// 六种语言中的简单函数示例
function (a) { return a > 0; } // JS
[](int a) { return a > 0; } // C++
(lambda (a) (> a 0)) ;; Lisp
lambda a: a > 0 # Python
a => a > 0 // C#
a -> a > 0 // Java
箭袋中的新羽
ES6中引入了一种编写函数的新语法
//ES5
var requestData=values.reduce(function(a,b){
return a+b;
},0)
//ES6
var requestData=values.reduce((a,b)=>a+b,0)
我认为这看起来酷毙了。
正如你使用类似Underscore.js和Immutable.js这样的库提供的函数工具,箭头函数运行起来同样美不可言。事实上,Immutable的文档中的示例全都由ES6写成,其中的许多特性已经用上了箭头函数。
那么不是非常函数化的情况又如何呢?除表达式外,箭头函数还可以包含一个块语句。回想一下我们之前的示例:
//ES5
$("#save").click(function(event){
requestData();
responseData();
})
这是它们在ES6中看起来的样子:
//ES6
$("#save").click(event=>{
requestData();
responseData();
})
这是一个微小的改进,对于使用了Promises的代码来说箭头函数的效果可以变得更加戏剧性,}).then(function (result) { 这样的一行代码可以堆积起来。
注意,使用了块语句的箭头函数不会自动返回值,你需要使用return语句将所需值返回。
小提示:当使用箭头函数创建普通对象时,你总是需要将对象包裹在小括号里。
// 为与你玩耍的每一个小狗创建一个新的空对象
var chewToys = puppies.map(puppy => {}); // 这样写会报Bug!
var chewToys = puppies.map(puppy => ({})); //
用小括号包裹空对象就可以了。
不幸的是,一个空对象{}和一个空的块{}看起来完全一样。ES6中的规则是,紧随箭头的{被解析为块的开始,而不是对象的开始。因此,puppy => {}这段代码就被解析为没有任何行为并返回undefined的箭头函数。
更令人困惑的是,你的JavaScript引擎会将类似{key: value}的对象字面量解析为一个包含标记语句的块。幸运的是,{是唯一一个有歧义的字符,所以用小括号包裹对象字面量是唯一一个你需要牢记的小窍门。
这个函数的this值是什么呢?
普通function函数和箭头函数的行为有一个微妙的区别,箭头函数没有它自己的this值,箭头函数内的this值继承自外围作用域。
在我们尝试说明这个问题前,先一起回顾一下。
JavaScript中的this是如何工作的?它的值从哪里获取?这些问题的答案可都不简单,如果你对此倍感清晰,一定因为你长时间以来一直在处理类似的问题。
这个问题经常出现的其中一个原因是,无论是否需要,function函数总会自动接收一个this值。你是否写过这样的hack代码:
{
...
addAll: function addAll(pieces) {
var self = this;
_.each(pieces, function (piece) {
self.add(piece);
});
},
...
}
在这里,你希望在内层函数里写的是this.add(piece),不幸的是,内层函数并未从外层函数继承this的值。在内层函数里,this会是window或undefined,临时变量self用来将外部的this值导入内部函数。(另一种方式是在内部函数上执行.bind(this),两种方法都不甚美观。)
在ES6中,不需要再hackthis了,但你需要遵循以下规则:
- 通过object.method()语法调用的方法使用非箭头函数定义,这些函数需要从调用者的作用域中获取一个有意义的this值。
- 其它情况全都使用箭头函数。
// ES6
{
...
addAll: function addAll(pieces) {
_.each(pieces, piece => this.add(piece));
},
...
}
在ES6的版本中,注意addAll方法从它的调用者处获取了this值,内部函数是一个箭头函数,所以它继承了外围作用域的this值。
超赞的是,在ES6中你可以用更简洁的方式编写对象字面量中的方法,所以上面这段代码可以简化成:
// ES6的方法语法
{
...
addAll(pieces) {
_.each(pieces, piece => this.add(piece));
},
...
}
箭头函数与非箭头函数间还有一个细微的区别,箭头函数不会获取它们自己的arguments对象。诚然,在ES6中,你可能更多地会使用不定参数和默认参数值这些新特性。