写点什么

深入浅出 ES6(五):不定参数和默认参数

  • 2015-07-06
  • 本文字数:2618 字

    阅读完需:约 9 分钟

编者按:ECMAScript 6 已经正式发布了,作为它最重要的方言,Javascript 也即将迎来语法上的重大变革,InfoQ 特开设“深入浅出ES6 ”专栏,来看一下ES6 将给我们带来哪些新内容。本专栏文章来自 Mozilla Web 开发者博客,由作者授权翻译并发布。

今天这篇文章将为你带来两个使 JavaScript 函数语法更富表现力的新特性:不定参数和默认参数。

不定参数

我们通常使用可变参函数来构造 API,可变参函数可接受任意数量的参数。例如, String.prototype.concat 方法就可以接受任意数量的字符串参数。ES6 提供了一种编写可变参函数的新方式——不定参数。

我们通过一个简单的可变参数函数 containsAll 给大家演示不定参数的用法。函数 containsAll 可以检查一个字符串中是否包含若干个子串,例如:containsAll(“banana”, “b”, “nan”) 返回 true,containsAll(“banana”, “c”, “nan”) 返回 false。

首先使用传统方法来实现这个函数:

复制代码
function containsAll(haystack) {
for (var i = 1; i < arguments.length; i++) {
var needle = arguments[i];
if (haystack.indexOf(needle) === -1) {
return false;
}
}
return true;
}

在这个实现中,我们用到了神奇的 arguments 对象,它是一个类数组对象,其中包含了传递给函数的所有参数。这段代码实现了我们的需求,但它的可读性却不是最理想的。函数的参数列表中只有一个参数 haystack,我们无法一眼就看出这个函数实际上接受了多个参数。另外,我们一定要注意,应该从 1 开始迭代,而不是从 0 开始,因为 arguments[0] 相当于参数 haystack。如果我们想要在 haystack 前后添加另一个参数,我们一定要记得更新循环体。不定参数恰好可以解决可读性与参数索引的问题。下面是用 ES6 不定参数特性实现的 containsAll 函数:

复制代码
function containsAll(haystack, ...needles) {
for (var needle of needles) {
if (haystack.indexOf(needle) === -1) {
return false;
}
}
return true;
}

这一版 containsAll 函数与前者有相同的行为,但这一版中使用了一个特殊的…needles 语法。我们来看一下调用 containsAll(“banana”, “b”, “nan”) 之后的函数调用过程,与之前一样,传递进来的第一个参数"banana"赋值给参数 haystack,needles 前的省略号表明它是一个不定参数,所有传递进来的其它参数都被放到一个数组中,赋值给变量 needles。对于我们的调用示例而言,needles 被赋值为 [“b”, “nan”],后续的函数执行过程一如往常。(注意啦,我们已经使用过 ES6 中 for-of 循环。)

在所有函数参数中,只有最后一个才可以被标记为不定参数。函数被调用时,不定参数前的所有参数都正常填充,任何“额外的”参数都被放进一个数组中并赋值给不定参数。如果没有额外的参数,不定参数就是一个空数组,它永远不会是 undefined。

默认参数

通常来说,函数调用者不需要传递所有可能存在的参数,没有被传递的参数可由感知到的默认参数进行填充。JavaScript 有严格的默认参数格式,未被传值的参数默认为 undefined。ES6 引入了一种新方式,可以指定任意参数的默认值。

下面是一个简单的示例(反撇号表示模板字符串,上周已经讨论过。):

复制代码
function animalSentence(animals2="tigers", animals3="bears") {
return `Lions and ${animals2} and ${animals3}! Oh my!`;
}

默认参数的定义形式为 [param1[ = defaultValue1 ][, …, paramN[ = defaultValueN ]]],对于每个参数而言,定义默认值时 = 后的部分是一个表达式,如果调用者没有传递相应参数,将使用该表达式的值作为参数默认值。相关示例如下:

复制代码
animalSentence(); // Lions and tigers and bears! Oh my!
animalSentence("elephants"); // Lions and elephants and bears! Oh my!
animalSentence("elephants", "whales"); // Lions and elephants and whales! Oh my!

默认参数有几个微妙的细节需要注意:

  • 默认值表达式在函数调用时自左向右求值,这一点与 Python 不同。这也意味着,默认表达式可以使用该参数之前已经填充好的其它参数值。举个例子,我们优化一下刚刚那个动物语句函数:
复制代码
function animalSentenceFancy(animals2="tigers",
animals3=(animals2 == "bears") ? "sealions" : "bears")
{
return `Lions and ${animals2} and ${animals3}! Oh my!`;
}

现在,animalSentenceFancy(“bears”) 将返回“Lions and bears and sealions. Oh my!”。

  • 传递 undefined 值等效于不传值,所以 animalSentence(undefined, “unicorns”) 将返回“Lions and tigers and unicorns! Oh my!”。
  • 没有默认值的参数隐式默认为 undefined,所以

function myFunc(a=42, b) {...}是合法的,并且等效于

function myFunc(a=42, b=undefined) {...}## 停止使用 arguments

现在我们已经看到了 arguments 对象可被不定参数和默认参数完美代替,移除 arguments 后通常会使代码更易于阅读。除了破坏可读性外,众所周知,针对 arguments 对象对 JavaScript 虚拟机进行的优化会导致一些让你头疼不已的问题。

我们期待着不定参数和默认参数可以完全取代 arguments,要实现这个目标,标准中增加了相应的限制:在使用不定参数或默认参数的函数中禁止使用 arguments 对象。曾经实现过 arguments 的引擎不会立即移除对它的支持,当然,现在更推荐使用不定参数和默认参数。

浏览器支持

Firefox 早在第 15 版的时候就支持了不定参数和默认参数。

不幸的是,尚未有其它已发布的浏览器支持不定参数和默认参数。V8 引擎最近增添了针对不定参数的实验性的支持,并且有一个开放状态的V8 issue 给实现默认参数使用,JSC 同样也有一个开放的 issue 来给不定参数默认参数使用。

Babel Traceur 编译器都支持默认参数,所以从现在起就可以开始使用。

文后盘点

尽管技术上不支持任何新的行为,不定参数和参数默认值还是可以使一些 JavaScript 函数定义更富有表现力并且更加可读。调用时自然也更加舒爽!

鸣谢:感谢 Benjamin Peterson 在 Firefox 中实现了这些特性,同时感谢他对于整个项目的贡献,以及他用心撰写了本篇文章。

在下一篇文章中,我们将介绍另外一个简单、优雅、实用,同样是你每天都会用到的 ES6 特性。这篇文章中用到了你平时用来写数组和对象的熟悉的语法,并为这些语法润色,产生一个新的、简洁的方式来拆解数组和对象。那意味着什么呢?为什么要拆解对象?敬请期待 Mozilla 工程师 Nick Fitzgerald 为我们带来的《深入浅出 ES6 解构》。

2015-07-06 07:1651875
用户头像

发布了 63 篇内容, 共 133.2 次阅读, 收获喜欢 38 次。

关注

评论

发布
暂无评论
发现更多内容

实录 | MegEngine 大 Kernel 卷积工程优化实践

MegEngineBot

深度学习 开源 卷积 MegEngine 大 Kernel

软件测试 | 测试开发| 跨平台设备管理方案Selenium Grid

测吧(北京)科技有限公司

聊聊Cookie、Session、Token 背后的故事

华为云开发者联盟

前端 华为云 企业号 1 月 PK 榜

Excelize 2.7.0 发布, 2023 年首个更新

xuri

golang GitHub 开源 编程 Excelize

Hive查询语句

mm

Spring Boot 3.0横空出世,快来看看是不是该升级了

程序那些事

Java spring 程序那些事 spring boot3

解决Redis缓存穿透/击穿/雪崩以及数据一致性的方案

风铃架构日知录

Java redis 缓存穿透 缓存雪崩 数据一致性

桌面云是什么?有什么优势?桌面云是云桌面吗?

行云管家

云计算 桌面云 云桌面

让开源和标准成为云原生的确定性力量

阿里巴巴云原生

阿里云 开源 云原生

市面上数一数二的双机热备系统当属Skybility HA!

行云管家

高可用 厂商 双机热备 双机热备系统

一站式云原生体验|龙蜥云原生ACNS + Rainbond

北京好雨科技有限公司

Kubernetes 云原生

漏洞优先级排序的六大关键因素

SEAL安全

安全 漏洞 企业号 1 月 PK 榜 优先级排序

成功上岸字节全靠这份Redis技术笔记,深入浅出值得一看

小小怪下士

Java redis 程序员 面试 字节

ThreadLocal源码解析及实战应用

京东科技开发者

Java 源码 技术 后端 企业号 1 月 PK 榜

软件测试/测试开发 | 跨平台设备管理方案 Selenium Grid

测试人

软件测试 自动化测试 测试开发 selenium Grid

使用服务网格提升应用和网络安全

HummerCloud

服务网格 云原生安全

基于单机最高能效270亿参数GPT模型的文本生成与理解

阿里云大数据AI技术

自然语言处理 机器学习 GPT 企业号 1 月 PK 榜

不懂任务调度系统,快来看这篇

华为云开发者联盟

后端 开发 华为云 企业号 1 月 PK 榜

从一个Demo说起Dubbo3

宋小生

dubbo RPC Dubbo3

KubeVela 获得 2022 “开源新锐”和“开发者最喜爱”双料年度项目

阿里巴巴云原生

阿里云 开源 云原生

送给SQL开发者的一份新年礼物!一款100%自主研发的纯Web化SQL开发工具——SQL Studio 1.0正式发布

雨果

sql 数据库管理工具 SQL开发工具

4个因素会影响LED显示屏的安全防火问题

Dylan

LED显示屏 全彩LED显示屏 led显示屏厂家

使用无代码构建移动应用程序

间隔

Apache Spark + 海豚调度:PB 级数据调度挑战,教你如何构建高效离线工作流

白鲸开源

海豚调度 Apache Spark 大数据 开源

年度重磅!《2022华为开发者宝典》免费下载

华为云开发者联盟

开源 华为云 鲲鹏 昇腾 企业号 1 月 PK 榜

全景剖析阿里云容器网络数据链路(二):Terway EN

阿里巴巴云原生

阿里云 容器 云原生

简单聊聊Redis中的几种Java客户端,以及它们的优缺点!

程序员小毕

数据库 redis 程序员 面试 后端

EMQX企业版正式入驻华为云云商城,成为华为云联营联运合作伙伴

EMQ映云科技

物联网 IoT 华为云 云端 企业号 1 月 PK 榜

全景剖析阿里云容器网络数据链路(一):Flannel

阿里巴巴云原生

阿里云 容器 云原生

【Redis 技术探索】「数据迁移实战」手把手教你如何实现在线 + 离线模式进行迁移 Redis 数据实战指南(scan模式迁移)

洛神灬殇

redis 数据同步 1月日更 RedisShake

DNS 代理?Pipy:这我也可以

Flomesh

Pipy 可编程代理 流量管理

深入浅出ES6(五):不定参数和默认参数_JavaScript_Benjamin Peterson_InfoQ精选文章