js 中以 ... 为前缀的几种用法详解
作者:亦黑迷失 发布时间:2024-04-18 09:40:38
ES6 开始,js 新增了剩余参数语法、展开语法等,它们有个共同之处就是都以 ...
这么个符号为前缀,好像很多地方都可以用到,但实际上又不是同一回事,容易让初学者晕头转向。本篇就对目前笔者已知的 3 种关于 ...
用法的总结归纳。
用法一:函数的剩余参数(Rest parameters)
在定义函数的时候,形参当中,最后一个参数(theArgs
)如果以 ...
作为前缀,那么 theArgs
就会变为一个数组,调用该函数时,如果传入的实参数量大于等于定义函数时形参的数量,比如下面的例 1.1,形参定义了 3 个,却传入了 5 个实参,那么从 3 开始(包括),之后的实参都会被放入到 theArgs
中:
// 例 1.1
function fn(a, b, ...theArgs) {
console.log(theArgs)
}
fn(1, 2, 3, 4, 5) // [ 3, 4, 5 ]
请注意,...theArgs
必须作为最后一个形参,不然会报语法错误:“SyntaxError: Rest parameter must be last formal parameter”。 除箭头函数外,函数内都有个 arguments 可以用来获取传递给函数的参数。那么它和剩余参数有什么区别呢?
剩余参数,比如例 1.1 中的
theArgs
是个真数组,可以直接使用数组的方法,而 arguments 是一个类数组(array-like)对象,想要运用数组方法还得转成数组;剩余参数只包含那些没有对应形参的实参,比如例 1.1 中的 3、4 和 5,而 arguments 包含了传给函数的所有实参。
arguments 可以通过 callee 属性获取当前 arguments 所在的函数。
其实 arguments 是早期的规范中为了方便获取传入函数的参数而存在的,ES6 之后,我们应当尽量使用剩余参数这种语法而不是 arguments。
用法二:展开语法(Spread syntax)
展开语法又可以在 3 个地方使用:
1)调用函数时
剩余参数是在函数定义时形参里使用的,展开语法的用处之一则是在函数调用时,传入的实参里使用。具体说就是将数组表达式或者 string 在语法层面展开作为实参传给函数。比如有如下代码:
// 例 2.1
const arr = [1, 2, 3]
const string = 'Jay'
function fn(a, b, c) {
console.log(a, b, c)
}
fn
接收 3 个参数,如果我们想把数组 arr
中的每个元素传给 fn
,在 ES6 之前可以利用 apply 实现:
// 例 2.1.1
fn.apply(null, arr) // 1 2 3
有了展开语法,则可以直接将作为参数的数组 arr
展开,这样可以提高代码的可读性 :
// 例 2.1.2
fn(...arr) // 1 2 3
展开语法还可以将字符串展开:
// 例 2.1.3
fn(...string) // J a y
2)构造数组时
通过字面量方式构造数组时,也可以使用展开语法:
// 例 2.2
const arr1 = [1, 2]
const arr2 = [3, 4, 5]
// 构造字面量数组
const newArr = [...arr1, ...arr2]
console.log(newArr) // [ 1, 2, 3, 4, 5 ]
3)构造字面量对象时
除了构造数组,ES2018(ES9) 添加了新的特性使得构造对象的时候也可以运用展开语法,被展开的对象表达式将按 key-value 的方式展开 :
// 例 2.3.1
const obj = { name: 'Jay' }
const newObj = { ...obj, age: 40 }
console.log(newObj) // { name: 'Jay', age: 40 }
在构造对象字面量时,还可以把数组也放进去展开,比如在 MDN 上关于展开语法的说明中有这么个例子:
// 例 2.3.2
const obj1 = { foo: 'bar', x: 42 }
const obj2 = { foo: 'baz', y: 13 }
const merge = (...objects) => ({ ...objects })
const mergedObj = merge(obj1, obj2)
console.log(mergedObj) // { '0': { foo: 'bar', x: 42 }, '1': { foo: 'baz', y: 13 } }
乍看上去迷迷糊糊的,可能的难点在于第 4 行代码的理解,它定义了一个函数 const merge = (...objects) => ({ ...objects }
, 其中=>
左边的 ...objects
为函数的剩余参数语法,所以 objects
就是一个由 obj1 和 obj2 这两个对象组成的数组:[ { foo: 'bar', x: 42 }, { foo: 'baz', y: 13 } ]
;=>
右边的 ...objects
用到的是展开语法,用于构造字面量对象,只不过因为 objects
是个数组,所以展开数组得到的对象的 key 为数组的下标,即 { ...objects }
得到的对象为:{ '0': { foo: 'bar', x: 42 }, '1': { foo: 'baz', y: 13 } }
。
注意
在调用函数或构造数组时使用展开语法只能用于可迭代对象,比如不能在构造数组时把一个对象放进去使用展开语法。
展开语法其实是一种浅拷贝
在 MDN 文档还能看到这么一句话:
实际上, 展开语法和 Object.assign() 行为一致, 执行的都是浅拷贝(只遍历一层)。
也就是说类似下面的例 2.4:
// 例 2.4
const obj = {
a: 'a',
b: {
c: 'd'
}
}
const newObj = { ...obj }
newObj.b.c = '我变了'
console.log(obj.b.c) // 我变了
我们改变通过展开语法得到的 newObj 的深层属性 newObj.b.c = '我变了'
时,会导致 obj.b.c
的值一同被修改。原因可以通过画内存表现图说明:
const newObj = { ...obj }
生成的 newObj,只是把堆内存中 obj 指向的地址为 0x100 这个对象整个复制了一份,生成了地址为 0x300 的新对象(注意这个新对象里,b 的值是一个内存地址),然后让 newObj 指向它。如果我们更改了 newObj 的 a 属性或 b 属性的值,当然不会影响到 obj 对象,但是如果我们改变的是 newObj.b.c
,就会找到 0x200 这个对象并修改,自然会影响到 obj 对象。
用法三:解构数组时
在对数组进行解构赋值时,我们可以使用剩余模式,将剩余数组赋值给一个变量,效果上类似于函数的剩余参数。比如下面例 3.1 中的 c
:
// 例 3.1
const arr = [1, 2, 3, 4, 5]
const [a, b, ...c] = arr
console.log(c) // [ 3, 4, 5 ]
来源:https://juejin.cn/post/7148415918303870983


猜你喜欢
- 专家解答 通过查询任何数据库中的三个系统表,你可以获得每个表的每一个字段的默认值。下面是这个核心查询。它返回分配给当前数据库中每个用户表的默
- 一、什么是主键、外键: 关系型数据库中的一条记录中有若干个属性,若其中某一个属性组(注意是组)能唯一标识一条记录,该属性组就可以成为一个主键
- 方法一 delete from test; alter table `test` auto_increment=1 (好处,可以设置AUTO
- 首先介紹一下我們用360搜索派取城市排名前20。我们爬取的网址:https://baike.so.com/doc/24368318-2518
- golang支持两种随机数生成方式:math/rand // 伪随机cr
- 1、说明当函数/方法执行出现异常,会将异常传递给函数/方法的调用一方。如果传递到主程序,仍然没有异常处理,程序才会被终止。2、实例def d
- 本文实例讲述了Vue开发之封装上传文件组件与用法。分享给大家供大家参考,具体如下:使用elementui的 el-upload插件实现图片上
- 前言本文大概内容:例如:随着ElementUI前后端交互的技术的更新,用户的的体验越来越好。本文主要针对用户在保持原页面结构,再添加另一个页
- ALTER TABLE 表名字 ADD CONSTRAINT pk_表名字 PRIMARY KEY( SNumber, SDate ); S
- 共轭梯度法是介于最速下降法与牛顿法之间的一个方法,它仅需利用一阶导数信息,但克服了最速下降法收敛慢的缺点,又避免了牛顿法需要存储和计算Hes
- 本文为大家分享一个简单商城购物车的python代码,供大家参考,具体内容如下要求:1、写一段商城程购物车序的代码2、用列表把商城的商品清单存
- 一、模块简介Beautiful Soup 是一个可以从HTML或XML文件中提取数据的Python库.它能够通过你喜欢的转换器实现惯用的文档
- js关于 byval 与 byref 二者区别: byval 传递数值,实参和形参分处不同的内存单元,互不干扰! byref 传递地址,实参
- DOM模型中的节点:元素节点、文本节点、属性节点 例:<a href=”http://www.cnblogs.com/shuz”>
- 在处理图像的时候经常是读取图片以后把图片转换为灰度图。作为一个刚入坑的小白,我在这篇博客记录了四种处理的方法。首先导入包:import nu
- 输入框组默认是div.input-group已知可在input表单元素前后加入两类元素【分别是文本和按钮】如下所示:div.input-gr
- 我是闲的没事干,2014过的太浮夸了,博客也没写几篇,哎~~~ 用这篇来记录即将逝去的2014python对各种数据库的各种操作满大街都是,
- MSSQL差异备份,就是和前一次备份作对比,把不一样的内容备份下来,这样,只要前一次备份后,插入新的内容,差异备份就可以把刚插入的内容备份出
- 1,关闭eslint这里只说vue-cli脚手架的关闭方法,其实很简单,就是把 build/webpack.base.conf.js 配置文
- 我为什么做这项工作?其实这项工作是另一位同事在做,过程中发下了一些问题,但是种种原因log和数据都没有收集到,无法进行分析。然后我就接手了,