深拷贝和浅拷贝
1. 为什么需要拷贝
JavaScript 中的对象(包括数组、函数等)是引用类型。直接使用赋值操作(let obj2 = obj1)并不会复制对象本身,而是复制了一个指向同一内存地址的引用。修改 obj2 的属性会同时影响到 obj1。
javascript
const original = { a: 1, b: { c: 2 } };
const copy = original;
copy.a = 100;
console.log(original.a); // 100 —— 原对象被改变了!为了避免这种“意外联动”,我们需要创建对象的拷贝(副本)。根据拷贝的深度不同,分为浅拷贝和深拷贝。
2. 浅拷贝(Shallow Copy)
2.1 定义
浅拷贝创建一个新对象,这个对象拥有原对象属性值的一份精确副本。
- 如果属性是基本数据类型(number, string, boolean, null, undefined, symbol),拷贝的是值;
- 如果属性是引用类型(对象、数组、函数等),拷贝的是内存地址(引用)。因此,修改新对象的引用类型属性会影响原对象。
2.2 常见的浅拷贝方法
方法1:展开运算符(Spread)...
javascript
const original = { a: 1, b: { c: 2 } };
const shallow = { ...original };
shallow.a = 100; // 修改基本类型,不影响原对象
shallow.b.c = 200; // 修改嵌套对象的属性,原对象也会改变
console.log(original.b.c); // 200方法2:Object.assign()
javascript
const shallow = Object.assign({}, original);方法3:数组浅拷贝
arr.slice()arr.concat()[...arr]Array.from(arr)
3. 深拷贝(Deep Copy)
3.1 定义
深拷贝递归地拷贝对象的所有层级,新对象与原对象在内存中完全独立。修改新对象的任何属性都不会影响原对象。
3.2 实现深拷贝的方法
方法1:JSON.parse(JSON.stringify(obj))(最常用,但有局限性)
javascript
const original = { a: 1, b: { c: 2 } };
const deep = JSON.parse(JSON.stringify(original));
deep.b.c = 200;
console.log(original.b.c); // 2 —— 不受影响局限性(面试常考):
- 无法拷贝
undefined、function、Symbol(它们会被忽略或转为null); - 无法拷贝循环引用的对象(会抛出错误);
- 无法拷贝
Date、RegExp、Map、Set、Blob等特殊对象(会变成普通对象或字符串); - 无法拷贝对象的原型链(
__proto__丢失)。
javascript
const obj = {
fn: function() {}, // 被忽略
undef: undefined, // 被忽略
sym: Symbol('foo'), // 被忽略
date: new Date() // 变成字符串
};
const cloned = JSON.parse(JSON.stringify(obj));
console.log(cloned); // { date: '2025-...' }方法2:使用 structuredClone()(现代浏览器/Node 17+)
structuredClone 是浏览器原生提供的深拷贝 API,支持大多数内置类型(Date、RegExp、Map、Set、ArrayBuffer 等),也支持循环引用。
不支持:Function、Symbol、DOM 元素 等。
javascript
const original = {
map: new Map([['key', 'value']]),
date: new Date(),
circle: null
};
original.circle = original; // 循环引用
const deep = structuredClone(original);
console.log(deep.map === original.map); // false方法3:使用第三方库 lodash
优点:稳定、全面、性能好。缺点:引入额外体积。
javascript
const _ = require('lodash');
const deep = _.cloneDeep(original);方法4:递归实现(手写深拷贝)
javascript
function deepClone(obj, hash = new WeakMap()) {
// 处理 null 或非对象
if (obj === null || typeof obj !== 'object') return obj;
// 处理循环引用
if (hash.has(obj)) return hash.get(obj);
// 处理日期、正则
if (obj instanceof Date) return new Date(obj);
if (obj instanceof RegExp) return new RegExp(obj);
// 处理数组或对象
const cloneObj = Array.isArray(obj) ? [] : {};
hash.set(obj, cloneObj);
// 遍历自身及继承的可枚举属性(可根据需求调整)
for (let key in obj) {
if (obj.hasOwnProperty(key)) {
cloneObj[key] = deepClone(obj[key], hash);
}
}
return cloneObj;
}