5.5 对象与数组的深拷贝技巧
5.5 对象与数组的深拷贝技巧
在 JavaScript 中,对象和数组是引用类型,直接赋值或使用扩展运算符(...)只能实现浅拷贝。深拷贝是指创建一个新的对象或数组,并递归地复制所有嵌套的对象和数组,确保修改新对象不会影响原对象。本节将介绍几种实现深拷贝的常用技巧。
5.5.1 浅拷贝 vs 深拷贝
1. 浅拷贝
浅拷贝只复制对象或数组的第一层属性,嵌套的对象或数组仍然是引用。
const original = { a: 1, b: { c: 2 } };
const shallowCopy = { ...original };
shallowCopy.b.c = 3;
console.log(original.b.c); // 输出 3(原对象被修改)
2. 深拷贝
深拷贝会递归地复制所有嵌套的对象和数组,确保新对象与原对象完全独立。
const original = { a: 1, b: { c: 2 } };
const deepCopy = JSON.parse(JSON.stringify(original));
deepCopy.b.c = 3;
console.log(original.b.c); // 输出 2(原对象未被修改)
5.5.2 实现深拷贝的常用方法
1. 使用 JSON.parse 和 JSON.stringify
这是最简单的深拷贝方法,但有以下限制:
- 不能复制函数、
undefined、Symbol等特殊类型。 - 不能处理循环引用(即对象内部引用自身)。
const original = { a: 1, b: { c: 2 } };
const deepCopy = JSON.parse(JSON.stringify(original));
2. 使用递归函数
通过递归遍历对象或数组,手动实现深拷贝。
function deepClone(obj) {
if (obj === null || typeof obj !== "object") {
return obj;
}
if (Array.isArray(obj)) {
const arrCopy = [];
for (let item of obj) {
arrCopy.push(deepClone(item));
}
return arrCopy;
}
const objCopy = {};
for (let key in obj) {
if (obj.hasOwnProperty(key)) {
objCopy[key] = deepClone(obj[key]);
}
}
return objCopy;
}
const original = { a: 1, b: { c: 2 } };
const deepCopy = deepClone(original);
3. 使用第三方库
许多第三方库(如 Lodash)提供了深拷贝功能,支持处理复杂对象和循环引用。
const _ = require("lodash");
const original = { a: 1, b: { c: 2 } };
const deepCopy = _.cloneDeep(original);
5.5.3 处理特殊情况的深拷贝
1. 处理循环引用
循环引用是指对象内部引用自身,直接使用递归会导致栈溢出。可以通过缓存已拷贝的对象来解决:
function deepClone(obj, cache = new WeakMap()) {
if (obj === null || typeof obj !== "object") {
return obj;
}
if (cache.has(obj)) {
return cache.get(obj);
}
const clone = Array.isArray(obj) ? [] : {};
cache.set(obj, clone);
for (let key in obj) {
if (obj.hasOwnProperty(key)) {
clone[key] = deepClone(obj[key], cache);
}
}
return clone;
}
const original = { a: 1 };
original.self = original; // 循环引用
const deepCopy = deepClone(original);
2. 处理特殊类型
如果需要复制函数、undefined、Symbol 等特殊类型,可以在递归函数中增加处理逻辑:
function deepClone(obj, cache = new WeakMap()) {
if (obj === null || typeof obj !== "object") {
return obj;
}
if (cache.has(obj)) {
return cache.get(obj);
}
const clone = Array.isArray(obj) ? [] : {};
cache.set(obj, clone);
for (let key in obj) {
if (obj.hasOwnProperty(key)) {
clone[key] = deepClone(obj[key], cache);
}
}
// 处理 Symbol 键
const symbols = Object.getOwnPropertySymbols(obj);
for (let sym of symbols) {
clone[sym] = deepClone(obj[sym], cache);
}
return clone;
}
5.5.4 示例代码
示例 1:使用 JSON.parse 和 JSON.stringify
const original = { a: 1, b: { c: 2 } };
const deepCopy = JSON.parse(JSON.stringify(original));
deepCopy.b.c = 3;
console.log(original.b.c); // 输出 2
示例 2:递归实现深拷贝
function deepClone(obj) {
if (obj === null || typeof obj !== "object") {
return obj;
}
if (Array.isArray(obj)) {
const arrCopy = [];
for (let item of obj) {
arrCopy.push(deepClone(item));
}
return arrCopy;
}
const objCopy = {};
for (let key in obj) {
if (obj.hasOwnProperty(key)) {
objCopy[key] = deepClone(obj[key]);
}
}
return objCopy;
}
const original = { a: 1, b: { c: 2 } };
const deepCopy = deepClone(original);
deepCopy.b.c = 3;
console.log(original.b.c); // 输出 2
示例 3:使用 Lodash 实现深拷贝
const _ = require("lodash");
const original = { a: 1, b: { c: 2 } };
const deepCopy = _.cloneDeep(original);
deepCopy.b.c = 3;
console.log(original.b.c); // 输出 2
5.5.5 总结
- 浅拷贝:只复制对象或数组的第一层属性。
- 深拷贝:递归地复制所有嵌套的对象和数组。
- 实现方法:
JSON.parse和JSON.stringify:简单但有限制。- 递归函数:灵活但需要处理特殊情况。
- 第三方库(如 Lodash):功能强大且易用。
通过掌握深拷贝的技巧,你可以更好地管理对象和数组的复制,避免意外的副作用。在接下来的学习中,我们将继续探索 JavaScript 的其他高级特性。
思考题:
JSON.parse和JSON.stringify实现深拷贝的局限性是什么?- 如何处理循环引用的深拷贝问题?
- 在什么情况下应该使用第三方库实现深拷贝?
#前端开发
分享于 2025-03-19
上一篇:5.4 扩展运算符(...)的应用
下一篇:6.1 回调函数与回调地狱问题