展开运算符实现浅拷贝仅复制第一层属性或元素,基本类型值拷贝、引用类型地址拷贝,不遍历嵌套结构,也不处理不可枚举属性、原型链及symbol键。

JavaScript 中展开运算符(...)实现浅拷贝时,并不涉及“深层层级遍历”或“递归复制”,它只是按对象或数组的**第一层属性/元素**,逐个读取并写入新对象或数组。底层执行顺序本质上是“读—赋值”过程,依赖于目标数据类型的可迭代性与属性访问机制。
展开运算符对数组的浅拷贝执行顺序
当用于数组(如 [...arr]),引擎会:
- 确认
arr是可迭代对象(具有Symbol.iterator方法); - 调用其迭代器,按索引顺序依次取出第
0、1、2… 项的值(即arr[0]、arr[1]等); - 将每个值(不进行类型检查或嵌套处理)直接赋给新数组对应位置;
- 新数组长度与原数组一致,但所有元素均为原值的**内存引用拷贝**(基本类型是值拷贝,引用类型是地址拷贝)。
展开运算符对对象的浅拷贝执行顺序
当用于对象(如 {...obj}),引擎会:
- 获取
obj的**自身可枚举属性键**(不含原型链上的属性,也不含不可枚举或 Symbol 属性,除非显式使用Object.getOwnPropertyNames+Object.getOwnPropertySymbols组合); - 按属性键的遍历顺序(ES2015+ 规定:数字索引升序 → 其他字符串键插入顺序 → Symbol 键插入顺序)依次读取每个键对应的值;
- 将键值对以“赋值语句”形式写入新对象(等价于
newObj[key] = obj[key]); - 该过程不触发 setter、不校验属性描述符(如
writable)、不处理 getter(仅取其返回值)。
为什么叫“浅”拷贝?关键在值的传递方式
所谓“浅”,指只拷贝第一层的值,而不会深入到嵌套对象或数组内部:
- 如果原数组中某项是普通对象(如
{a: 1}),展开后新数组中该项仍是该对象的同一引用; - 修改新数组中这个对象的属性(如
newArr[2].a = 99),原数组对应位置的对象也会同步变化; - 这和
Object.assign({}, obj)或Array.from(arr)行为一致,都是单层属性/元素的直接赋值。
注意:展开不是语法糖,而是迭代协议驱动的运行时行为
展开运算符不是简单的源码替换,它依赖 ES6 迭代协议:
- 对类数组对象(如
arguments、NodeList),只要部署了Symbol.iterator就能展开; - 自定义对象可通过实现
[Symbol.iterator]()控制展开时产出哪些值; - 若目标不可迭代(如
null、undefined、普通函数),运行时抛出TypeError。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











