
本文详解 javascript 中数组赋值时因引用传递导致的“反向覆盖”问题,揭示 obj.a = obj.b 实际复制的是内存地址而非数据本身,并提供多种安全深拷贝方案(如扩展运算符、slice()、array.from() 等),助开发者避免意外状态污染。
本文详解 javascript 中数组赋值时因引用传递导致的“反向覆盖”问题,揭示 obj.a = obj.b 实际复制的是内存地址而非数据本身,并提供多种安全深拷贝方案(如扩展运算符、slice()、array.from() 等),助开发者避免意外状态污染。
在 JavaScript 中,数组和对象属于引用类型(reference types),这意味着变量存储的并非数据本身,而是指向堆内存中实际数据的引用(指针)。当执行 obj.var1 = obj.var2 时,你并未创建新数组,而是让 var1 和 var2 共同指向同一块内存地址。后续对任一变量所指向数组的修改(例如 obj.var1[1] = 99),都会直接影响另一变量——这正是示例中“恢复备份后却出现 [1, 99, 1]”的根本原因。
以下代码清晰复现该问题:
var obj = {
var1: [],
var2: []
};
obj.var1 = [1, 10, 1];
obj.var2 = obj.var1; // ❌ 浅拷贝:var2 与 var1 共享引用
obj.var1 = [0, 2, 0]; // 新值,不影响 var2(因 var1 已重新赋值)
obj.var1 = obj.var2; // ✅ 此时 var1 再次指向原数组 → 两者仍同源
console.log("1.", obj.var1, obj.var2); // [1, 10, 1], [1, 10, 1]
obj.var1[1] = 99; // ⚠️ 修改共享数组的第2项
obj.var1 = obj.var2; // ✅ 再次赋值(无实际效果,仍指向同一数组)
console.log("2.", obj.var1, obj.var2); // [1, 99, 1], [1, 99, 1] ← 意外同步变更!要真正实现独立备份与安全恢复,必须进行浅层深拷贝(shallow copy)——即为数组创建一个新实例,其元素值相同但内存地址独立。以下是推荐的现代、可靠方案:
✅ 推荐的浅拷贝方法(适用于一维数组)
| 方法 | 语法 | 特点 |
|---|---|---|
| 扩展运算符 | obj.var1 = [...obj.var2]; | 语义清晰、性能好、ES6+ 标准 |
| slice() | obj.var1 = obj.var2.slice(); | 兼容性极佳(IE9+) |
| Array.from() | obj.var1 = Array.from(obj.var2); | 支持类数组对象,语义明确 |
| concat() | obj.var1 = obj.var2.concat(); | 传统方式,稍显冗余 |
✅ 正确修复后的逻辑示例:
立即学习“Java免费学习笔记(深入)”;
obj.var1 = [1, 10, 1]; obj.var2 = [...obj.var1]; // ✅ 创建独立副本 obj.var1[1] = 99; // 只影响 var1 console.log(obj.var1); // [1, 99, 1] console.log(obj.var2); // [1, 10, 1] ← 备份完好! obj.var1 = [...obj.var2]; // ✅ 安全恢复原始值 console.log(obj.var1); // [1, 10, 1] ← 如预期
⚠️ 注意事项与进阶提示
- 仅限一维数组:上述方法均为浅拷贝,若数组内含嵌套对象或数组(如 [[1,2], {x:3}]),需使用 structuredClone()(现代浏览器)、JSON.parse(JSON.stringify())(简单场景)或 Lodash 的 _.cloneDeep()。
- 避免 obj.var1 = obj.var2.slice(0) 写法:虽有效,但 slice() 无参更简洁且语义更准确。
- 全局对象风险:示例中将 var2 用作跨函数调用的“全局备份”,务必确保每次备份都使用深拷贝,否则状态污染会随引用持续蔓延。
- 性能考量:对于超大数组(>10⁵ 元素),扩展运算符与 slice() 性能接近;若需极致优化,可考虑 new Uint8Array() 等底层方案,但绝大多数业务场景无需过度优化。
总之,理解 JavaScript 的引用本质是写出健壮状态管理代码的第一步。永远警惕 = 对对象/数组的赋值行为——它不是复制数据,而是共享内存。用 ...、slice() 或 Array.from() 主动创建副本,才能真正掌控数据生命周期。










