如何在JavaScript中实现可靠的嵌套对象深度克隆函数?
寻找JavaScript中可靠的深度克隆方案
我需要一个能完整克隆对象(包括所有嵌套属性和数组)的深度克隆函数。试过JSON.parse(JSON.stringify(obj)),但它存在诸多局限(比如无法处理函数、循环引用、特殊对象类型),而且我自己写的递归函数也没达到预期效果——修改克隆后的对象或其嵌套属性会影响原对象。
我的递归实现代码:
function deepClone(obj) { if (typeof obj !== 'object' || obj === null) { return obj; } let clone = Array.isArray(obj) ? [] : {}; for (let key in obj) { if (obj.hasOwnProperty(key)) { clone[key] = deepClone(obj[key]); } } return clone; } // 使用示例: let originalObj = { name: 'John', age: 30, address: { street: '123 Main St', city: 'New York', state: 'NY' }, hobbies: ['reading', 'cooking', 'swimming'] }; let clonedObj = deepClone(originalObj);
改进版深度克隆实现
你的递归代码在处理普通嵌套对象/数组时理论上是有效的,出现修改克隆体影响原对象的情况,大概率是原对象包含了循环引用、特殊类型(如Date、RegExp),或是测试操作有误。以下是覆盖更多场景的稳健实现:
function deepClone(obj, visited = new WeakMap()) { // 处理基本类型与null if (obj === null || typeof obj !== 'object') { return obj; } // 处理循环引用,避免无限递归 if (visited.has(obj)) { return visited.get(obj); } // 处理特殊内置对象类型 if (obj instanceof Date) { return new Date(obj.getTime()); } if (obj instanceof RegExp) { return new RegExp(obj.source, obj.flags); } if (obj instanceof Map) { const cloneMap = new Map(); visited.set(obj, cloneMap); obj.forEach((value, key) => { cloneMap.set(deepClone(key, visited), deepClone(value, visited)); }); return cloneMap; } if (obj instanceof Set) { const cloneSet = new Set(); visited.set(obj, cloneSet); obj.forEach(value => { cloneSet.add(deepClone(value, visited)); }); return cloneSet; } // 保留原对象的原型链 const clone = Array.isArray(obj) ? [] : Object.create(Object.getPrototypeOf(obj)); visited.set(obj, clone); // 遍历所有自有属性(含不可枚举属性) Object.getOwnPropertyNames(obj).forEach(key => { clone[key] = deepClone(obj[key], visited); }); // 处理Symbol类型的属性 Object.getOwnPropertySymbols(obj).forEach(sym => { clone[sym] = deepClone(obj[sym], visited); }); return clone; }
核心优势
- 解决循环引用:通过WeakMap记录已克隆对象,避免无限递归和引用混乱
- 支持特殊对象类型:正确克隆Date、RegExp、Map、Set等内置对象
- 保留原型链:克隆对象继承原对象的原型结构
- 覆盖全量属性:包括不可枚举属性和Symbol类型属性
验证测试
用复杂对象测试克隆效果:
let originalObj = { name: 'John', age: 30, address: { street: '123 Main St', city: 'New York', state: 'NY' }, hobbies: ['reading', 'cooking', 'swimming'], birthDate: new Date('1993-01-01'), regex: /test/i, map: new Map([['key1', 'value1']]), set: new Set([1, 2, 3]) }; let clonedObj = deepClone(originalObj); // 修改克隆对象的嵌套属性 clonedObj.address.street = '456 Oak Ave'; clonedObj.hobbies.push('hiking'); clonedObj.birthDate.setFullYear(1994); console.log(originalObj.address.street); // 输出 '123 Main St',原对象未被修改 console.log(originalObj.hobbies); // 输出 ['reading', 'cooking', 'swimming'],原数组未被修改 console.log(originalObj.birthDate.getFullYear()); // 输出 1993,原日期未被修改
如果你的原代码出现异常,建议检查:
- 是否在测试时误操作了原对象
- 原对象是否包含循环引用或特殊类型(原代码未处理这些场景)
内容的提问来源于stack exchange,提问作者Haitham6373
相关产品推荐
相关产品推荐

