如何在不使用递归及现成方法的情况下实现对象深拷贝
如何实现不使用递归、JSON方法和structuredClone的完整深拷贝?
原代码存在的问题
你的deepCopy函数有两个核心问题导致无法完成完整深拷贝:
- 数组处理逻辑错误:直接将数组的第一个元素作为处理对象,完全忽略了数组本身的结构,无法正确拷贝数组类型的输入。
- 仅支持两层嵌套:只手动遍历了一层嵌套对象,更深层级的对象/数组会直接赋值引用,没有进行迭代拷贝,导致深层数据仍共享原对象的内存地址。
迭代式深拷贝实现(非递归)
我们可以用**栈(Stack)**来实现迭代式的深拷贝,遍历所有嵌套层级,同时处理对象、数组、基本类型,还能避免循环引用问题:
function deepCopy(source) { // 处理非对象/数组的情况:直接返回原值 if (source === null || typeof source !== 'object') { return source; } // 初始化拷贝的根结构:数组或对象 const target = Array.isArray(source) ? [] : {}; // 用Map记录已拷贝的对象,避免循环引用导致死循环 const copiedMap = new Map(); copiedMap.set(source, target); // 栈结构:每个元素保存 [原数据节点, 目标数据节点] const stack = [[source, target]]; while (stack.length > 0) { const [currentSource, currentTarget] = stack.pop(); // 遍历当前节点的所有键(数组是索引,对象是属性名) const keys = Array.isArray(currentSource) ? Array.from({ length: currentSource.length }, (_, i) => i) : Object.keys(currentSource); for (const key of keys) { const value = currentSource[key]; // 基本类型(包括null、undefined)直接赋值 if (value === null || typeof value !== 'object') { currentTarget[key] = value; continue; } // 处理已拷贝过的对象(循环引用) if (copiedMap.has(value)) { currentTarget[key] = copiedMap.get(value); continue; } // 创建新的嵌套结构(数组或对象) const newTarget = Array.isArray(value) ? [] : {}; copiedMap.set(value, newTarget); currentTarget[key] = newTarget; // 将新节点压入栈,继续处理深层嵌套 stack.push([value, newTarget]); } } return target; }
测试验证
用你提供的示例数据测试:
const data1 = { a: 10, y: null, k: [1, 2], b: 3, c: { d: 4, e: { w: 50, u: undefined, s: { f: { v: 5, }, }, }, }, }; const data2 = [ { a: 10, y: null, k: [1, 2], b: 3, c: { d: 4, e: { w: 50, u: undefined, s: { f: { v: 5, }, }, }, }, }, ]; const copy1 = deepCopy(data1); const copy2 = deepCopy(data2); // 验证深层修改不影响原对象 copy1.c.e.s.f.v = 100; console.log(data1.c.e.s.f.v); // 输出5,原对象未被修改 console.log(copy1.c.e.s.f.v); // 输出100,拷贝对象已修改 copy2[0].k[0] = 99; console.log(data2[0].k[0]); // 输出1,原数组未被修改 console.log(copy2[0].k[0]); // 输出99,拷贝数组已修改
核心逻辑说明
- 栈迭代:通过栈保存待处理的节点,替代递归的调用栈,实现对任意深度嵌套结构的遍历。
- 类型区分:正确识别数组和对象类型,创建对应的空结构进行拷贝。
- 循环引用处理:用
Map记录已经拷贝过的对象,避免循环引用导致的死循环和重复拷贝。 - 全类型覆盖:支持基本类型(number、string、boolean、null、undefined)、对象、数组的拷贝。
内容的提问来源于stack exchange,提问作者akula
相关产品推荐
相关产品推荐

