JavaScript中的深拷贝与浅拷贝
字数 651 2025-11-03 08:33:38
JavaScript中的深拷贝与浅拷贝
描述
在JavaScript中,深拷贝和浅拷贝是处理对象复制的两种不同方式。浅拷贝只复制对象的引用,不复制对象本身,新旧对象共享同一块内存地址。深拷贝则是创建一个全新的对象,并递归复制原对象的所有属性,新旧对象完全独立,互不影响。理解这两种拷贝方式的区别对于避免意外的数据修改至关重要。
知识讲解
1. 基本概念区分
- 浅拷贝:只复制第一层属性,如果属性是引用类型,复制的只是内存地址
- 深拷贝:完全复制整个对象结构,包括所有嵌套的引用类型属性
2. 赋值、浅拷贝、深拷贝的对比
让我们先创建一个测试对象:
const originalObj = {
name: "小明",
age: 20,
hobbies: ["篮球", "游泳"],
address: {
city: "北京",
district: "海淀区"
}
};
赋值操作(不是拷贝):
const assignedObj = originalObj;
assignedObj.name = "小红";
console.log(originalObj.name); // "小红" - 原对象也被修改了
3. 浅拷贝的实现方式
方法一:Object.assign()
const shallowCopy1 = Object.assign({}, originalObj);
shallowCopy1.name = "小刚"; // 修改基本类型属性
console.log(originalObj.name); // "小明" - 原对象未受影响
shallowCopy1.hobbies.push("读书"); // 修改嵌套的引用类型属性
console.log(originalObj.hobbies); // ["篮球", "游泳", "读书"] - 原对象被影响了!
方法二:展开运算符
const shallowCopy2 = {...originalObj};
shallowCopy2.address.city = "上海";
console.log(originalObj.address.city); // "上海" - 原对象被影响了!
4. 深拷贝的实现方式
方法一:JSON.parse(JSON.stringify())(最简单但有限制)
const deepCopy1 = JSON.parse(JSON.stringify(originalObj));
deepCopy1.hobbies.push("绘画");
console.log(originalObj.hobbies); // ["篮球", "游泳"] - 原对象不受影响
局限性:
const problemObj = {
date: new Date(),
func: function() {},
undefined: undefined,
infinity: Infinity,
regex: /abc/gi
};
const problematicCopy = JSON.parse(JSON.stringify(problemObj));
console.log(problematicCopy);
// date变成字符串,function和undefined丢失,Infinity变成null
方法二:手写递归深拷贝函数
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);
}
// 处理数组
if (Array.isArray(obj)) {
const result = [];
hash.set(obj, result);
obj.forEach(item => {
result.push(deepClone(item, hash));
});
return result;
}
// 处理普通对象
const result = {};
hash.set(obj, result);
Object.keys(obj).forEach(key => {
result[key] = deepClone(obj[key], hash);
});
return result;
}
5. 测试深拷贝函数
const testObj = {
name: "测试",
nested: {
array: [1, 2, { value: 3 }]
}
};
// 创建循环引用
testObj.self = testObj;
const clonedObj = deepClone(testObj);
clonedObj.nested.array.push(4);
console.log(testObj.nested.array); // [1, 2, {value: 3}] - 原对象不受影响
console.log(clonedObj.self === clonedObj); // true - 循环引用正确处理
6. 实际应用场景
适合浅拷贝的情况:
- 对象结构简单,没有嵌套引用类型
- 只需要第一层属性的独立副本
- 性能要求较高,数据量较大
适合深拷贝的情况:
- 需要完全独立的对象副本
- 对象结构复杂,有多层嵌套
- 需要修改副本而不影响原对象
总结
深拷贝和浅拷贝是JavaScript中重要的概念。在实际开发中,应根据具体需求选择合适的拷贝方式。对于简单场景可以使用JSON方法,对于复杂场景建议使用成熟的工具库(如Lodash的_.cloneDeep)或自己实现完整的深拷贝函数。