数组做参数_ES6 系列:你不知道的 Rest 参数与 Spread 语法细节
Rest 参数与 Spread 语法
在 JavaScript 中,很多内建函数都支持传入任意数量的参数。
例如:
Math.max(arg1, arg2, ..., argN)
—— 返回入参中的最大值。Object.assign(dest, src1, ..., srcN)
—— 依次将属性从src1..N
复制到dest
。- ……等。
在本文中,我们将学习如何编程实现支持函数可传入任意数量的参数。以及,如何将数组作为参数传递给这类函数。
Rest 参数 ...
在 JavaScript 中,无论函数是如何定义的,你都可以使用任意数量的参数调用函数。
例如:
function sum(a, b) { return a + b;}
alert( sum(1, 2, 3, 4, 5) );
虽然这里不会因为传入“过多”的参数而报错。但是当然,在结果中只有前两个参数被计算进去了。
Rest 参数可以通过使用三个点 ...
并在后面跟着包含剩余参数的数组名称,来将它们包含在函数定义中。这些点的字面意思是“将剩余参数收集到一个数组中”。
例如,我们需要把所有的参数都放到数组 args
中:
function sumAll(...args) { // 数组名为 args let sum = 0;
for (let arg of args) sum += arg;
return sum;}
alert( sumAll(1) ); // 1alert( sumAll(1, 2) ); // 3alert( 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");
Rest 参数必须放到参数列表的末尾
Rest 参数会收集剩余的所有参数,因此下面这种用法没有意义,并且会导致错误:
function f(arg1, ...rest, arg2) { // arg2 在 ...rest 后面?! // error}
...rest
必须处在最后。
"arguments" 变量
有一个名为 arguments
的特殊的类数组对象,该对象按参数索引包含所有参数。
例如:
function showName() { alert( arguments.length ); alert( arguments[0] ); alert( arguments[1] );
// 它是可遍历的 // for(let arg of arguments) alert(arg);}
// 依次显示:2,Julius,CaesarshowName("Julius", "Caesar");
// 依次显示:1,Ilya,undefined(没有第二个参数)showName("Ilya");
在过去,JavaScript 中没有 rest 参数,而使用 arguments
是获取函数所有参数的唯一方法。现在它仍然有效,我们可以在一些老代码里找到它。
但缺点是,尽管 arguments
是一个类数组,也是可迭代对象,但它终究不是数组。它不支持数组方法,因此我们不能调用 arguments.map(...)
等方法。
此外,它始终包含所有参数,我们不能像使用 rest 参数那样只截取入参的一部分。
因此,当我们需要这些功能时,最好使用 rest 参数。
箭头函数是没有 "arguments"
如果我们在箭头函数中访问
arguments
,访问到的arguments
并不属于箭头函数,而是属于箭头函数外部的“普通”函数。举个例子:
function f() { let showArg = () => alert(arguments[0]); showArg();} f(1); // 1 我们已经知道,箭头函数没有自身的 `this`。现在我们知道了它们也没有特殊的 `arguments` 对象。
Spread 语法
我们刚刚看到了如何从参数列表中获取数组。
不过有时候我们也需要做与之相反的事儿。
例如,内建函数 Math.max[1] 会返回参数中最大的值:
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(arg[0], arg[1], arg[2])
,因为我们不确定这儿有多少个。在脚本执行时,可能参数数组中有很多个元素,也可能一个都没有。并且这样设置的代码也很丑。
Spread 语法 来帮助你了!它看起来和 rest 参数很像,也使用 ...
,但是二者的用途完全相反。
当在函数调用中使用 ...arr
时,它会把可迭代对象 arr
“展开”到参数列表中。
以 Math.max
为例:
let arr = [3, 5, 1];
alert( Math.max(...arr) ); // 5(spread 语法把数组转换为参数列表)
我们还可以通过这种方式传递多个可迭代对象:
let arr1 = [1, -2, 3, 4];let arr2 = [8, 3, -8, 1];
alert( Math.max(...arr1, ...arr2) ); // 8
我们甚至还可以将 spread 语法与常规值结合使用:
let arr1 = [1, -2, 3, 4];let arr2 = [8, 3, -8, 1];
alert( Math.max(1, ...arr1, 2, ...arr2, 25) ); // 25
并且,我们还可以使用 spread 语法来合并数组:
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)
在上面的示例中,我们使用数组展示了 spread 语法,其实任何可迭代对象都可以。
例如,在这儿我们使用 spread 语法将字符串转换为字符数组:
let str = "Hello";
alert( [...str] ); // H,e,l,l,o
Spread 语法内部使用了迭代器来收集元素,与 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
适用于类数组对象也适用于可迭代对象。- Spread 语法只适用于可迭代对象。
因此,对于将一些“东西”转换为数组的任务,Array.from
往往更通用。
获取一个 array/object 的副本
还记得我们 之前讲过的[2]Object.assign()
吗?
使用 spread 语法也可以做同样的事情。
let arr = [1, 2, 3];let arrCopy = [...arr]; // 将数组 spread 到参数列表中 // 然后将结果放到一个新数组
// 两个数组中的内容相同吗?alert(JSON.stringify(arr) === JSON.stringify(arrCopy)); // true
// 两个数组相等吗?alert(arr === arrCopy); // false(它们的引用是不同的)
// 修改我们初始的数组不会修改副本:arr.push(4);alert(arr); // 1, 2, 3, 4alert(arrCopy); // 1, 2, 3
并且,也可以通过相同的方式来复制一个对象:
let obj = { a: 1, b: 2, c: 3 };let objCopy = { ...obj }; // 将对象 spread 到参数列表中 // 然后将结果返回到一个新对象
// 两个对象中的内容相同吗?alert(JSON.stringify(obj) === JSON.stringify(objCopy)); // true
// 两个对象相等吗?alert(obj === objCopy); // false (not same reference)
// 修改我们初始的对象不会修改副本:obj.d = 4;alert(JSON.stringify(obj)); // {"a":1,"b":2,"c":3,"d":4}alert(JSON.stringify(objCopy)); // {"a":1,"b":2,"c":3}
这种方式比使用 let arrCopy = Object.assign([], arr);
来复制数组,或使用 let objCopy = Object.assign({}, obj);
来复制对象写起来要短得多。因此,只要情况允许,我们更喜欢使用它。
总结
当我们在代码中看到 "..."
时,它要么是 rest 参数,要么就是 spread 语法。
有一个简单的方法可以区分它们:
- 若
...
出现在函数参数列表的最后,那么它就是 rest 参数,它会把参数列表中剩余的参数收集到一个数组中。 - 若
...
出现在函数调用或类似的表达式中,那它就是 spread 语法,它会把一个数组展开为列表。
使用场景:
- Rest 参数用于创建可接受任意数量参数的函数。
- Spread 语法用于将数组传递给通常需要含有许多参数的列表的函数。
它们俩的出现帮助我们轻松地在列表和参数数组之间来回转换。
“旧式”的 arguments
(类数组且可迭代的对象)也依然能够帮助我们获取函数调用中的所有参数。
现代 JavaScript 教程:开源的现代 JavaScript 从入门到进阶的优质教程。React 官方文档推荐,与 MDN 并列的 JavaScript 学习教程[3]。
在线免费阅读:https://zh.javascript.info
参考资料
[1]
Math.max: https://developer.mozilla.org/zh/docs/Web/JavaScript/Reference/Global_Objects/Math/max
[2]
之前讲过的: https://zh.javascript.info/object-copy#ke-long-yu-he-bing-objectassign
[3]
React 官方文档推荐,与 MDN 并列的 JavaScript 学习教程: https://zh-hans.reactjs.org/docs/getting-started.html#javascript-resources
最后
如果你觉得这篇内容对你挺有启发,我想邀请你帮我三个小忙:
点个「在看」,让更多的人也能看到这篇内容(喜欢不点在看,都是耍流氓 -_-)
欢迎加我微信「qianyu443033099」拉你进技术群,长期交流学习...
关注公众号「前端下午茶」,持续为你推送精选好文,也可以加我为好友,随时聊骚。
点个在看支持我吧,转发就更好了
数组做参数_ES6 系列:你不知道的 Rest 参数与 Spread 语法细节相关推荐
- ES6 系列:你不知道的 Rest 参数与 Spread 语法细节
Rest 参数与 Spread 语法 在 JavaScript 中,很多内建函数都支持传入任意数量的参数. 例如: Math.max(arg1, arg2, ..., argN) -- 返回入参中的最 ...
- ES6 进阶:你不知道的 Rest 参数与 Spread 语法细节
Rest 参数与 Spread 语法 在 JavaScript 中,很多内建函数都支持传入任意数量的参数. 例如: Math.max(arg1, arg2, ..., argN) -- 返回入参中的最 ...
- myBatis的xml映射文件中传入list集合与数组做条件
mybatis的xml映射文件中传入list集合与数组做条件 1.传list集合参数 1.1sql映射: <select id="queryDeptListByBankLevelAnd ...
- C语言-二维数组做函数的参数
文章目录 1 引例 2 观点1 这种使用方法是错误的 3 观点2 根本不需要这么做 4 二维数组做函数参数的方法 4.1 方法1 4.2 方法2 4.3 方法3 5 与Java的不同 1 引例 下面的 ...
- C语言多维数组做函数参数技术推演
多维数组做函数参数技术 C语言中只会以机械式的值拷贝的方式传递参数(实参把值传给形参) 二维数组参数同样存在退化的问题 等价关系 C语言中只会以机械式的值拷贝的方式传递参数(实参把值传给形参) int ...
- C语言多维数组做函数参数退化原因大剖析
多维数组做函数参数退化原因 多维数组做函数参数退化原因大剖析 多维数组做函数参数退化原因大剖析 //证明一下多维数组的线性存储 //线性打印 void printfArray411(int *arra ...
- C语言中数组做函数参数的问题
数组做函数参数,会退化成为一个指针变量.因此在进行数组参数传递的同时,需要传递一个数组长度的参数变量. 数组长度可以通过sizeof(arr)/siezof(arr[0])来得到.关于这个sizeof ...
- java 11:数组作为函数参数,数组做为函数返回值
1 数组作为参数 我们可以将数组作为参数,传入到函数中,其实就像我们main函数中 public void main(String [] args){};就是用数组作为函数参数: 又如, [java] ...
- vector做函数参数和普通数组做函数参数有什么区别
结论: 1,vector做函数参数,当vector参数做输出(vector数组需要在函数中被改变)的时候,需要传入vector的引用. 2,vector做函数参数,当vector参数做输入参数(只使用 ...
最新文章
- ArcGIS for Desktop入门教程_第二章_Desktop简介 - ArcGIS知乎-新一代ArcGIS问答社区
- 第一天开通博客,记录自己在编程道路上的点点滴滴
- 法国呼叫服务公司Aircall获得800万美元融资
- IE6-IE9兼容性问题列表及解决办法_补遗漏之一:button的type默认值改变为submit了。...
- PostgreSQL与MySQL比较
- Java:多线程之线程池
- php redis ip查找,php+redis实现ip白名单并提供可配置ip页面
- 【深度学习】如何配置一台深度学习工作站?
- 初识Docker-什么是docker
- 谁动了我的选择器?深入理解CSS选择器优先级
- 【SpringBoot基础知识】如何在springboot中使用多线程
- HDU 2068 Choose the best route
- 浅评-我所用的输入法
- 【C++】常用查找算法
- C++虚函数(多态性)
- 如何写一份好的软件开发的需求分析
- mongodb-b站黑马程序员
- PAT --- 1055.集体照 (25 分)
- Reaver无线破解工具——穷举PIN码破解简析
- 目录操作 递归打印目录 DIR drent