深入理解JavaScript中的箭头函数


Posted in Javascript onJuly 28, 2015

从一开始箭头就是 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。

Javascript 相关文章推荐
JS数学函数Exp使用说明
Aug 09 Javascript
jquery实现固定顶部导航效果(仿蘑菇街)
Mar 21 Javascript
js如何获取file控件的完整路径具体实现代码
May 15 Javascript
基于OO的动画附加插件,可以实现弹跳、渐隐等动画效果 分享
Jun 24 Javascript
js实现简单折叠、展开菜单的方法
Aug 28 Javascript
实例讲解jQuery EasyUI tree中state属性慎用
Apr 01 Javascript
BootStrap学习系列之Bootstrap Typeahead 组件实现百度下拉效果(续)
Jul 07 Javascript
BootStrap Fileinput的使用教程
Dec 30 Javascript
JS简单获取当前日期时间的方法(如:2017-03-29 11:41:10 星期四)
Mar 29 Javascript
JS 组件系列之 bootstrap treegrid 组件封装过程
Apr 28 Javascript
设置cookie指定时间失效(实例代码)
May 28 Javascript
JavaScript 程序错误Cannot use 'in' operator to search的解决方法
Jul 10 Javascript
解析JavaScript的ES6版本中的解构赋值
Jul 28 #Javascript
深入学习JavaScript中的Rest参数和参数默认值
Jul 28 #Javascript
JQuery实现鼠标滚轮滑动到页面节点
Jul 28 #Javascript
CSS3实现动态背景登录框的代码
Jul 28 #Javascript
javascript制作幻灯片(360度全景图片)
Jul 28 #Javascript
详解JavaScript ES6中的模板字符串
Jul 28 #Javascript
javascript解决IE6下hover问题的方法
Jul 28 #Javascript
You might like
PHP 面向对象程序设计(oop)学习笔记(三) - 单例模式和工厂模式
2014/06/12 PHP
CI框架中$this-&gt;load-&gt;library()用法分析
2016/05/18 PHP
yum命令安装php7和相关扩展
2016/07/04 PHP
thinkphp中多表查询中防止数据重复的sql语句(必看)
2016/09/22 PHP
ubutu 16.04环境下,PHP与mysql数据库,网页登录验证实例讲解
2017/07/20 PHP
function, new function, new Function之间的区别
2007/03/08 Javascript
用JavaScript调用WebService的示例
2008/04/07 Javascript
javascript 45种缓动效果 非常酷
2011/06/28 Javascript
jQuery EasyUI API 中文文档 - EasyLoader 加载器
2011/09/29 Javascript
jQuery向上遍历DOM树之parents(),parent(),closest()之间的区别
2013/12/02 Javascript
jquery基础教程之deferred对象使用方法
2014/01/22 Javascript
使用jQuery实现input数值增量和减量的方法
2015/01/24 Javascript
使用jQueryMobile实现滑动翻页效果的方法
2015/02/04 Javascript
node-webkit打包成exe文件被360误报木马的解决方法
2015/03/11 Javascript
利用jQuery实现打字机字幕效果实例代码
2016/09/02 Javascript
CSS3+JavaScript实现翻页幻灯片效果
2017/06/28 Javascript
AngularJS基于provider实现全局变量的读取和赋值方法
2017/06/28 Javascript
layui弹出层按钮提交iframe表单的方法
2018/08/20 Javascript
javascript设计模式 ? 外观模式原理与用法实例分析
2020/04/15 Javascript
[04:36]DOTA2国际邀请赛 ti3精彩集锦
2013/08/19 DOTA
python海龟绘图实例教程
2014/07/24 Python
python之文件的读写和文件目录以及文件夹的操作实现代码
2016/08/28 Python
将Django项目部署到CentOs服务器中
2018/10/18 Python
anaconda如何查看并管理python环境
2019/07/05 Python
python 画函数曲线示例
2019/12/04 Python
Python调用钉钉自定义机器人的实现
2020/01/03 Python
Python实现加密接口测试方法步骤详解
2020/06/05 Python
.net面试题
2016/09/17 面试题
Python面试题集
2012/03/08 面试题
高中毕业自我鉴定范文
2013/10/02 职场文书
承认错误的检讨书
2014/01/30 职场文书
五四青年节优秀演讲稿范文
2014/05/28 职场文书
服务员态度差检讨书
2014/10/28 职场文书
三好学生主要事迹材料
2015/11/03 职场文书
工作转正自我鉴定范文
2019/06/21 职场文书
【D4DJ】美少女DJ企划 动画将于明年冬季开播第2季
2022/04/11 日漫