[ES6深度解析]6:箭头函数

前世今生

<!-- 单行注释

箭头从一开始就是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。可以跳过键入functionreturn,以及一些括号、大括号和分号。

要编写一个有多个参数(或没有参数,或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将是windowundefined。临时变量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参数或默认值。

[ES6深度解析]6:箭头函数

上一篇:深入浅出Map、WeakMap、Set、WeakSet


下一篇:HDU7055 Yiwen with Sqc 推式子