原文链接:http://javascript.info/rest-parameters-spread-operator,translate with ❤️ by zhangbao.
许多 JavaScript 内置的函数支持任意数量的参数调用。
例如:
Math.max(arg1, arg2, ..., argN)
:返回 N 个参数里最大的一个。Object.assign(dest, src1, ..., srcN)
:将src1
到srcN
对象里的属性(包括String
和Symbol
类型的属性)复制到dest
。等等……
在这一章中,我们将学习如何做同样的事情。更重要的是,如何更舒服地使用这些函数和数组。
剩余参数 …
一个函数调用时可以使用任意数量的参数,无论函数定义的方式如何。
像这样:
function sum(a, b) {
return a + b;
}
alert( sum(1, 2, 3, 4, 5) );
调用参数时,多余的参数调用,并不会导致错误发生。结果只会头两个参数被接收。
剩余参数出现在定义函数的时候,用三个点 ...
表示,它们的字面意思是“将剩余的参数收集到一个数组中”。
例如,将所有的参数收集到 args
中:
function sumAll(...args) { // args is the name for the array
let sum = 0;
for (let arg of args) sum += arg;
return sum;
}
alert( sumAll(1) ); // 1
alert( sumAll(1, 2) ); // 3
alert( sumAll(1, 2, 3) ); // 6
我们可以选择将第一个参数作为变量,只收集其余的参数。
在这里,前两个参数进入变量,其余的则进入 titles
数组中:
function showName(firstName, lastName, ...titles) {
alert( firstName + ' ' + lastName ); // Julius Caesar
// 剩余参数进入了 titles 数组
// i.e. titles = ["Consul", "Imperator"]
alert( titles[0] ); // Consul
alert( titles[1] ); // Imperator
alert( titles.length ); // 2
}
showName("Julius", "Caesar", "Consul", "Imperator");
注:剩余参数必须作为最后一个参数出现
剩余参数就是收集所有剩余的参数,所以只能放在最后一个位置。因此下面使用的剩余参数的方式是错误的:
function f(arg1, ...rest, arg2) { // arg2 在 ...rest 之后?!
// 出错
}
再强调一边,剩余参数只能放在最后一个位置。
“arguments”变量
函数中,还存在一个特殊的类数组对象,叫 arguments
,依据索引包含所有参数。
例如:
function showName() {
alert( arguments.length );
alert( arguments[0] );
alert( arguments[1] );
// arguments 是可迭代的
// for(let arg of arguments) alert(arg);
}
// 展示: 2, Julius, Caesar
showName("Julius", "Caesar");
// 展示: 1, Ilya, undefined (没有第二个参数)
showName("Ilya");
在旧时代,剩余参数在语言中并不存在,使用 arguments
是获得函数所有参数的惟一方法,不管它们的总数是多少。
今天,它仍然有效。
但缺点是尽管参数都是类数组对象,并且是可迭代的,但它不是一个真正的数组,它不支持数组方法,所以我们不能调用诸如 arguments.map(...)
之类的方法对参数做处理。
而且,arguments
总是包含所有的参数。我们不能部分地捕获它们,就像我们用剩余参数所做的那样。
因此,当我们需要这些特性时,剩余参数是首选的。、
注:箭头函数中没有“arguments
”
如果我们在箭头函数里访问 arguments
,他会拿外部“正常”函数里的。
这是个例子:
function f() {
let showArg = () => alert(arguments[0]);
showArg();
}
f(1); // 1
正如我们所记得的,箭头函数没有它们自己的 this
,现在我们知道它们也没有特殊的 arguments
对象。
扩展运算符
我们刚刚看到了如何从参数列表中获取数组。
但有时我们需要做的恰恰相反。
例如,有一个内置函数 Math.max,用于从列表中返回最大的数字:
alert( Math.max(3, 5, 1) ); // 5
现在假设我们有一个数组 [3, 5, 1]
。我们怎么用 Math.max
调用它呢?
把它按照原样传递是行不通的,因为 Math.max
希望接收的是一个数字参数列表,而不是一个数组:
let arr = [3, 5, 1];
alert( Math.max(arr) ); // NaN
当然,我们不能在代码中手动列出所有项目 Math.max(arr[0],arr[1],arr[2])
,因为我们不能确定有多少个。当我们的脚本执行时,可能会有很多,又或者没有。这样代码就会变得很难看。
扩展运算符就是来解决这个问题的!它看起来类似于剩余参数,也使用 ...
,但却做相反的事情。
...arr
用在函数调用时,它表示将一个可迭代对象 arr
分解成一个参数列表。
用 Math.max
举个例子:
let arr = [1, 2, 3];
alert( Math.max(...arr) ); // 5(把数组展开成一个参数列表)
我们也可以通过这种方式传递多个可迭代对象:
let arr1 = [1, -2, 3, 4];
let arr2 = [8, 3, -8, 1];
alert( Math.max(...arr1, ...arr2) ); // 8
我们甚至可以将扩展运算符与正常值相结合:
let arr1 = [1, -2, 3, 4];
let arr2 = [8, 3, -8, 1];
alert( Math.max(1, ...arr1, 2, ...arr2, 25) ); // 25
此外,扩展操作符可以用于合并数组:
let arr = [3, 5, 1];
let arr2 = [8, 9, 15];
let merged = [0, ...arr, 2, ...arr2];
alert(merged); // 0,3,5,1,2,8,9,15 (0, 然后是 arr, 然后是 2, 最后是 arr2)
在上面的例子中,我们使用了一个数组来演示扩展操作符,其实任何可迭代对象都是可以的。
例如,在这里,我们使扩展操作符将字符串转换成字符数组:
let str = "Hello";
alert( [...str] ); // H,e,l,l,o
扩展运算符内部使用迭代器来收集元素,跟 for..of
的原理是一样的。
因此,对于一个字符串,for..of
返回字符 ...str
变成了 "H","e","l","l","o"
,字符列表被传递给数组初始化器 [...str]
。
对于这个特殊的任务,我们还可以使用 Array.from
,因为它能够将一个可迭代对象(比如字符串)转换成一个数组:
let str = "Hello";
// Array.from 将可迭代转换为数组
alert( Array.from(str) ); // H,e,l,l,o
结果与 [...str]
一样。但是 Array.from(obj)
和 [...obj]
之间有一个细微的差别:
Array.from
操作符既可以作用在类数组对象上,也可以作用在可迭代对象上。扩展运算符只能作用在可迭代对象上。
因此,对于将某些东西转换成数组的任务来说,使用 Array.from
更加普遍。
总结
当我们在代码中看到 ...
时,它要么是剩余参数,要么是扩展运算符。
有一种简单的方法可以区分它们:
当
...
出现在函数参数的末尾时,它就是“剩余参数”,收集其余的参数列表到一个数组中。当
...
出现在函数调用或者类似场景中,它就担作“扩展运算符”,将一个数组展开成一个参数列表。
使用方式:
剩余参数用于创建接收任意数量参数的函数。
扩展操作符用于将数组传递给通常需要大量参数列表的函数。
它们一起帮助在参数列表和数字参数之间轻松地切换。
调用函数时的所有参数列表也可以从“旧式”arguments
对象获取到:它是一个类数组、可迭代对象。
(完)