ES6 in Depth: Rest Parameters and Defaults
今天文章是关于JS函数中更具有表达力的两个特性:剩余参数和默认参数。
剩余参数
创建一个可变的函数作为API是个常见的需求,而这函数可接受任意个参数。例如,String.prototype.concat
方法可接受任意数量的字符串参数。利用剩余参数,ES6提供了一种编写可变函数的方式。
为了说明,让我们编写一个简单的可变函数containsAll
,用来检查一个字符串是否包含有一定数量的子串。例如,containsAll("banana", "b", "nan")
会返回 true
,而containsAll("banana", "c", "nan")
会返回false
。
这里是一个传统的实现方式:
1 2 3 4 5 6 7 8 9 |
|
这种实现方式使用了具有魔力的 arguments 对象,它是一个类似数组的对象,包含了传递给函数的所有参数。这代码确实可以达到我们所需求的目标,但是其可读性并不是最佳的。这函数的参数列表中只包括了一个参数haystack
,所以如果只看一眼的话,是不可能知道这函数实际上是支持多个参数的。另外,我们必须小心迭代方式,arguments需要是以 1 开始,而不是 0 ,因为 arguments[0] 对应于 haystack。如果我们需要在 haystack 之前或者之后添加额外的参数时,我们不得不要记得更新循环方法。剩余参数 可解决这些问题。下面是一种利用剩余参数的自然的ES6实现方式:
1 2 3 4 5 6 7 8 |
|
这个版本的函数拥有的行为和之前第一个的一样,但是它包含了特殊的...needles
语法。让我们看一下containsAll("banana", "b", "nan")
是怎么调用此函数的。参数 haystack
如平常一样接受的是传递的第一个参数,值为“banana”。needles
之前的省略号表示为一个剩余参数,所有其它的参数会组成一个数组被安排到变量needles
中。对于这个例子,needles
的值为["b", "nan"]
。函数则按正常执行。(注意,我们这里使用了ES6 for-of
的循环结构。)
只有函数的最后一个参数可以作为一个剩余参数。在一次调用中,剩余参数之前的参数会和平常一样。所有额外的参数会被放到数组中,并被分配给剩余参数。如果没有额外的参数,剩余参数会简单地被置为空数组;剩余参数不可能会是 undefined
。
默认参数
通常来说,一个函数并不需要调用所有由调用者传递给的参数,当没有传递参数时,可以智能地使用默认值来作用于这些参数。JS 也有顽固的表单默认参数,当参数没有被传递时会默认设置为default
。ES6 推出一种可以指定任意的默认参数的方法。
这里是例子。(其中音符号表示模板字符串,上周已经讨论过了。)
1 2 3 |
|
对于每个参数,当调用者没有传递值时,包含=
的部分是个表达式,会默认将值赋予到参数中。所以,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, ES6的默认值表达式会在每次函数调用时从左到右执行。这就意味着默认表达式可以使用参数之前的参数值。例如,我们可以让我们的动物句子更有想象力些:
1 2 3 4 5 |
|
然后,animalSentenceFancy("bears")
会返回"Lions and bears and sealins. Oh my!"
。
传递
undefined
会被认为等价于没有传递参数,因此,animalSentence(undefined, "unicorns")
会返回"Lions and tigers and unicorns! Oh my!"
。当参数传递一个没有值的变量时,其传递的值会被设置为
undefined
,所以,
1
|
|
是允许的,而且其等价于
1
|
|
关闭 arguments
我们现在看到,剩余和默认参数可能替代arguments对象的用处,并且,移除 arguments 通常会使得代码更加易读。除了影响可读性,arguments臭名昭著还因为它 影响JS的VM效率。
为了使得剩余和默认参数完成取代 arguments,首先第一步需要函数使用剩余或者默认参数,禁止使用 argumentes 对象。尽管支持 arguments 的特性最近不会被移除,但是现在应该尽量避免使用 arguments,而尽可能地使用剩余和默认参数。
浏览器支持
Firefox 从 版本15之后就支持 剩余和默认参数了。
不幸的是,没有其它发布的浏览器版本支持剩余和默认参数。V8 最近支持剩余参数,并且 默认参数 已经提到日程中。JSC 也将 剩余参数 和 默认参数 提到的日程中。
Babel 和 Traceur 解释器都是支持默认参数的,所以可以开始使用它们。
结论
尽管技术上并不允许其它新的行为,但是剩余参数和默认参数可以让JS函数声明更加具有表现力和可读性。欢迎来电!
Note: 感谢 Benjamin Peterson 为 Firefox 实现这些特性,包括他对项目的贡献,同样包括这周的文章教程。
下周,我们将会介绍另外 简单、强大、实现、常用的 ES6 特性。它语法类似于你已经使用的数组和对象,并会生成出一种新的、精确的数组和对象的分离方式。这意味着什么呢?为什么你需要分离对象呢?在下周三加入我们并一同来寻找,来自 Mozilla 工程师 Nick Fitzgerald 展示的 【深入ES6 的去结构化(析构)】。