欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

深入理解JavaScript中的箭头函数

程序员文章站 2022-05-30 17:52:10
从一开始箭头就是 javascript 的一部分,在第一个 javascript 中就建议将内联的脚本代码包裹在 html 的注释中,这可以防止那些不支持 javascri...

从一开始箭头就是 javascript 的一部分,在第一个 javascript 中就建议将内联的脚本代码包裹在 html 的注释中,这可以防止那些不支持 javascript 的浏览器错误滴将你的代码显示为明文。你也许写过下面这样的代码:

<script language="javascript">
<!--
  document.bgcolor = "brown"; // red
// -->
</script>
 
<script language="javascript">
<!--
  document.bgcolor = "brown"; // red
// -->
</script>

古老的浏览器将看到两个不被支持的标签和一段注释,只有支持 javascript 的新浏览器才会将其解析为 javascript 代码。

为了支持这个古怪的特性,浏览器的 javascript 引擎把 <!-- 作为一个单行注释的开始,这不是开玩笑的,这一直都是这门语言的一部分,并且至今还能用,不仅仅在 <script> 标签内的首行,而是在 javascript 代码的任何部位都可用,它甚至还能在 node 中使用。

凑巧的是,这种风格的注释在 es6 中首次被标准化。但这并不是我们将谈论的箭头。

--> 也表示一个单行注释,与 html 不同的是,在 html 中,--> 之前的部分是注释内容,而在 javascript 中,在 --> 之后的行才是注释。

只有当 --> 出现在一行的开始时,才表示该箭头是一个注释,因为在其他情况下,--> 是一个操作符(goes to)。

function countdown(n) {
 while (n-->0) // "n goes to zero"
  alert(n);
 blastoff();
}
 
function countdown(n) {
 while (n-->0) // "n goes to zero"
  alert(n);
 blastoff();
}

上面代码是真实能运行的。循环运行直到 n 为 0,这并不是 es6 的新特性,但结合我们熟悉的特性,这具有很强的误导性。你能搞明白上面代码的运行情况吗?你可以在 stack overflow 上找到相应的解答。

当然还有一个箭头,那就是小于等于操作符 <=,也许你还可以找到使用箭头的地方,但我们还是停下来,看一个我们从没见过的箭头:

  •     <!-- 单行注释
  •     --> goes to 操作符
  •     <= 小于等于操作符
  •     => ???

那么,=> 表示什么呢?这就是本文将讨论的话题。

首先,我们来谈谈函数。
无处不在的函数表达式

javascript 一个有趣的特点是,任何时候你需要一个函数,你可以很方便地创建它们。

例如,为一个按钮绑定点击事件:

$("#confetti-btn").click(
 
$("#confetti-btn").click(

jquery 的 .click() 方法需要一个函数作为参数,我们可以很方便地就地创建一个函数:

$("#confetti-btn").click(function (event) {
 playtrumpet();
 fireconfetticannon();
});

 
$("#confetti-btn").click(function (event) {
 playtrumpet();
 fireconfetticannon();
});

现在对我们来说,编写这样的代码是最自然的事了。但是在 javascript 流行起来之前,这种风格的代码看起来还是有些奇怪,因为在其他语言中都没有这样的特性。在 1958 年,lisp 就有了函数表达式,也叫 lambda 函数,而在存在多年的 c++、python、c# 和 java 中没有该特性。

现在,这四门语言都有了 lambda 表达式,而且新出现的语言都普遍内置了 lambda 表达式。如今 javascript 也支持该特性了,这必须感谢那些重度依赖 lambda 表达式的库的开发者,这推动了该特性被广泛采纳。

与其他几门语言相比,javascript 的语法略显冗长:

// 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
 
// 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 引入了一种新的语法来编写函数:

// es5
var selected = alljobs.filter(function (job) {
 return job.isselected();
});

// es6
var selected = alljobs.filter(job => job.isselected());

 
// 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
var total = values.reduce(function (a, b) {
 return a + b;
}, 0);
 
// es6
var total = values.reduce((a, b) => a + b, 0);

箭头函数还可以与一些工具函数库完美地配合使用,比如 underscore.js 和 immutable,事实上,immutable 文档中的例子全部都是使用 es6 编写,其中有很多已经使用到了箭头函数。

函数体除了使用一个表达式外,箭头函数还可以包含一个语句块,回忆之前我们提到过的例子:

// es5
$("#confetti-btn").click(function (event) {
 playtrumpet();
 fireconfetticannon();
});
 
// es5
$("#confetti-btn").click(function (event) {
 playtrumpet();
 fireconfetticannon();
});

下面是采用箭头函数的写法:

// es6
$("#confetti-btn").click(event => {
 playtrumpet();
 fireconfetticannon();
});
 
// es6
$("#confetti-btn").click(event => {
 playtrumpet();
 fireconfetticannon();
});

需要注意的是,使用语句块的箭头函数不会自动返回一个值,必须显式地使用 return 来返回一个值。

还有一个忠告,当使用箭头函数来返回一个对象时,始终使用括号将返回的对象括起来:

// create a new empty object for each puppy to play with
var chewtoys = puppies.map(puppy => {});  // bug!
var chewtoys = puppies.map(puppy => ({})); // ok

 
// create a new empty object for each puppy to play with
var chewtoys = puppies.map(puppy => {});  // bug!
var chewtoys = puppies.map(puppy => ({})); // ok

因为空对象 {} 与空语句块 {} 看上去一模一样,es6 将始终把紧跟在 => 后面的 { 当作语句块的开始,而不是一个对象的开始,那么 puppy => {} 就被解析为一个没有函数体的箭头函数,而且返回值为 undefined。