
本文详解 const arr2 = arr1(引用赋值)与 const arr2 = [...arr1](浅拷贝)的核心差异,通过代码实证说明二者在内存指向、修改影响及嵌套结构行为上的根本不同。
本文详解 `const arr2 = arr1`(引用赋值)与 `const arr2 = [...arr1]`(浅拷贝)的核心差异,通过代码实证说明二者在内存指向、修改影响及嵌套结构行为上的根本不同。
在 JavaScript 中,将一个数组赋值给另一个变量时,看似相似的两行代码却蕴含截然不同的语义和运行时行为:
const arr1 = [1, 2, 3, 4, 5]; const arr2 = [...arr1]; // ✅ 创建新数组:浅拷贝 const arr3 = arr1; // ⚠️ 复用原引用:指向同一内存地址
关键区别在于:前者生成一个独立的新数组实例,后者仅复制引用(即两个变量指向堆内存中的同一个数组对象)。
? 行为对比:一改全改 vs 互不影响
可通过严格相等(===)和元素修改来直观验证:
const arr = [1, 2, 3]; const arrA = [...arr]; // 浅拷贝 → 新数组 const arrB = arr; // 引用赋值 → 同一数组 console.log(arr === arrA); // false(不同对象) console.log(arr === arrB); // true (同一对象) // 修改 arrA 不影响 arr arrA[0] = 9; console.log(arr); // [1, 2, 3] console.log(arrA); // [9, 2, 3] // 修改 arrB 等同于修改 arr arrB[0] = 9; console.log(arr); // [9, 2, 3] console.log(arrB); // [9, 2, 3]
✅ 结论一:若需隔离操作、避免副作用(如函数内修改不应影响原始数据),必须使用 [...arr] 或其他浅拷贝方式(如 arr.slice()、Array.from(arr)、structuredClone()(深拷贝,ES2022+))。
立即学习“Java免费学习笔记(深入)”;
⚠️ 注意:浅拷贝 ≠ 深拷贝
展开运算符 ... 实现的是浅拷贝——它仅复制数组第一层元素的值(对基本类型)或引用(对对象/嵌套数组)。深层嵌套结构仍共享引用:
const arr = [1, 2, [5, 6]]; const arrA = [...arr]; const arrB = arr; // 修改顶层元素:互不影响 arrA[0] = 8; // arr 保持 [1, 2, [5, 6]] console.log(arr[0]); // 1 // 修改嵌套数组内部:仍会联动! arrA[2][0] = 9; console.log(arr[2][0]); // 9 ← 被意外修改! console.log(arr); // [1, 2, [9, 6]] console.log(arrA); // [8, 2, [9, 6]]
这说明:[...arr] 对嵌套数组、对象等引用类型,拷贝的是其内存地址,而非递归创建副本。
? 实用建议与替代方案
| 场景 | 推荐方式 | 说明 |
|---|---|---|
| 简单一维数组(仅数字、字符串) | [...arr]、arr.slice() | 性能好、语法简洁 |
| 需要真正隔离所有层级(含对象/嵌套数组) | structuredClone(arr) | 原生深拷贝(现代浏览器 & Node.js ≥17.0),支持 Map/Set/Date 等 |
| 兼容旧环境 | JSON.parse(JSON.stringify(arr)) | 仅适用于可序列化数据(不支持函数、undefined、Symbol、循环引用) |
| 函数式编程习惯 | 始终避免直接修改输入数组,优先返回新数组 | 如使用 map、filter、concat 等不可变方法 |
✅ 总结
- const arr2 = arr1 是引用赋值:arr2 和 arr1 指向同一数组对象,任何一方修改都会反映到另一方;
- const arr2 = [...arr1] 是浅拷贝:创建新数组,顶层元素独立,但嵌套对象/数组仍共享引用;
- 判断是否需要拷贝?问自己:“后续是否可能修改 arr2,且不希望影响 arr1?” 若答案为是,就必须拷贝;若需完全隔离深层结构,则需深拷贝。
理解这一区别,是写出可预测、易调试、符合函数式思维的 JavaScript 数组操作代码的基础。










