前世今生

<!-- 单行注释

箭头从一开始就是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参数或默认值。