前世今生
<!-- 单行注释
箭头从一开始就是JavaScript的一部分。第一个JavaScript教程建议用HTML注释包装内联脚本。这将防止不支持JS的浏览器将JS代码错误地显示为文本。你可以这样写:
<script language="javascript">
<!--
document.bgColor = "brown"; // red
// -->
</script>
旧的浏览器会看到两个不支持的标签和一个评论;只有新的浏览器才会看到JS代码。为了支持这种奇怪的破解方法,浏览器中的JavaScript引擎将字符<!
作为一行注释的开头。没有玩笑。这实际上一直是语言的一部分,直到今天它还在工作,不仅在内联<script>
的顶部,而且在JS代码的所有地方。它甚至可以在Node中工作。
在ES6中,这种注释风格第一次被标准化了。
--> goes to 操作符
箭头序列-->
也表示一行注释。奇怪的是,在HTML中-->
之前的字符是注释的一部分,而在JS中-->
之后的行其余部分是注释。这个箭头只在注释出现在一行的开头时表示注释。这是因为在其他上下文中,-->
是JS中的一个操作符,“goes to”操作符!
function countdown(n) {
while (n --> 0) // "n goes to zero"
alert(n);
blastoff();
}
<= 小于等于符号
用来判断是否小于等于某数值
函数表达式无处不在
JavaScript的一个有趣特性是,当需要一个函数时,你可以在运行代码的过程中输入该函数。例如,假设试图告诉浏览器当用户单击某个按钮时该做什么。开始键入:
$("#confetti-btn").click(
jQuery的.click()
方法有一个参数:一个函数。没有问题。你可以在这里输入一个函数:
$("#confetti-btn").click(function (event) {
playTrumpet();
fireConfettiCannon();
});
现在,编写这样的代码对我们来说很自然。所以回想起来很奇怪,在JavaScript普及这种编程之前,许多语言都没有这个特性。当然,Lisp在1958年有函数表达式,也称为lambda函数。但是c++、Python、c#和Java都在没有它们的情况下存在了很多年。目前较新的语言都内置了lambda。我们要感谢JavaScript——以及早期JavaScript程序员无畏地构建了大量依赖lambda的库,从而导致了该特性的广泛采用。
然而,在我提到的所有语言中,JavaScript的lambda语法是最冗长的。
// A very simple function in six languages.
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新特性,箭头函数。
=> 箭头函数
ES6为编写函数引入了一种新的语法:
// ES5
var selected = allJobs.filter(function (job) {
return job.isSelected();
});
// ES6
var selected = allJobs.filter(job => job.isSelected());
当您只需要一个带参数的简单函数时,新的箭头函数语法就是Identifier => Expression
。可以跳过键入function
和return
,以及一些括号、大括号和分号。
要编写一个有多个参数(或没有参数,或rest参数或默认参数,或解构参数)的函数,您需要在参数列表周围添加圆括号:
// ES5
var total = values.reduce(function (a, b) {
return a + b;
}, 0);
// ES6
var total = values.reduce((a, b) => a + b, 0);
箭头函数可以包含一个语句块而不仅仅是一个表达式。回想一下我们之前的例子:
// ES5
$("#confetti-btn").click(function (event) {
playTrumpet();
fireConfettiCannon();
改为ES6箭头函数的形式:
// ES6
$("#confetti-btn").click(event => {
playTrumpet();
fireConfettiCannon();
});
注意,带有块体{}
的箭头函数不会自动返回值。此时要使用return
语句,显式返回。
使用箭头函数返回创建的普通对象时需要注意一点。总是用括号括住对象:
var chewToys = puppies.map(puppy => {}); // BUG!
var chewToys = puppies.map(puppy => ({})); // ok
不幸的是,空对象{}
和空块{}
看起来完全一样。ES6中的规则是,紧跟着箭头的{
总是被视为块的开始,而不是对象的开始。代码puppy =>{}
因此被静默地解释为一个不做任何事情并返回undefined的箭头函数。
更令人困惑的是,像{key: value}
这样的对象字面量看起来就像一个包含标签语句的块--至少在JavaScript引擎看来是这样的。幸运的是{
是唯一的二义字符,所以用括号包装对象字面量是你需要记住的唯一技巧。
this 是什么?
普通函数函数和箭头函数在行为上有一个细微的区别。箭头函数没有自己的this值。在箭头函数中this的值总是继承自外围作用域。
this
在JavaScript中是如何工作的?其值从何而来?没有简单的答案。如果这在你的脑海中看起来很简单,那是因为你已经处理它很长时间了!
这个问题经常出现的一个原因是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中,如果你遵循以下规则,这些Hack代码就会消失:
// ES6
{
...
addAll: function addAll(pieces) {
_.each(pieces, piece => this.add(piece));
},
...
}
在ES6版本中,注意addAll
方法从它的调用者那里接收this
。内部函数是一个箭头函数,因此它从外围作用域继承了this
。作为奖励,ES6还提供了一种更短的方法来编写对象字面量中的方法!所以上面的代码可以进一步简化:
// ES6 with method syntax
{
...
addAll(pieces) {
_.each(pieces, piece => this.add(piece));
},
...
}
在旧function
和箭头函数
之间,我可能再也不会键入functoin了。
箭头函数和非箭头函数之间还有一个细微的区别:箭头函数也没有自己的参数对象arguments
。当然,在ES6中,您可能更愿意使用rest参数或默认值。