915 字
5 分钟
JavaScript Deep Clone

题目描述
实现一个健壮的深拷贝函数,要求:
- 支持所有 JavaScript 数据类型(基础类型/对象/数组/Map/Set等)
- 处理循环引用问题(对象相互引用)
- 保留对象原型链关系
- 正确处理特殊对象(Date, RegExp 等)
- 处理 Symbol 类型属性
- 可配置克隆深度(可选)
要求完整实现深拷贝逻辑,并通过测试用例验证。
解题思路
- 类型判断:区分基础类型和引用类型
- 特殊对象处理:Date/RegExp 等创建新实例
- 原型继承:使用
Object.create
保持原型链 - 循环引用检测:使用 WeakMap 存储已拷贝对象
- 递归拷贝:深度遍历对象属性
- 容器对象处理:Map/Set/Array 单独处理
- Symbol 支持:遍历 Symbol 类型属性
关键洞察
- 循环引用处理:WeakMap 存储已拷贝对象防止无限递归
- 原型保持:
Object.getPrototypeOf
+Object.create
保持原型链 - 特殊对象克隆:Date/RegExp 等需要重建实例
- 容器对象区分:Map/Set/Array 需要特殊处理
- Symbol 支持:
Object.getOwnPropertySymbols
获取 Symbol 属性 - 性能优化:WeakMap 自动释放内存避免泄漏
代码流程
flowchart TD A[开始克隆] --> B{类型判断} B -->|基础类型| C[直接返回] B -->|引用类型| D[检查缓存] D -->|已存在| E[返回缓存值] D -->|未缓存| F[特殊对象处理] F -->|Date/RegExp等| G[创建新实例] F -->|Map/Set| H[遍历并递归克隆] F -->|Array/Object| I[创建空对象] I --> J[遍历所有属性] J --> K[递归克隆属性值] K --> L[存入缓存] L --> M[返回克隆对象]
代码实现
function deepClone(target, map = new WeakMap(), depth = Infinity) {// 处理基础类型和函数 if (target === null || typeof target !== 'object' || depth < 0) { return target; }
// 处理循环引用 if (map.has(target)) { return map.get(target); }
// 处理特殊对象 if (target instanceof Date) return new Date(target); if (target instanceof RegExp) return new RegExp(target);
// 获取原型对象 const prototype = Object.getPrototypeOf(target); const clone = Array.isArray(target) ? [] : prototype === Object.prototype ? Object.create(prototype) : Object.create(prototype);
// 缓存已拷贝对象 map.set(target, clone);
// 处理 Map if (target instanceof Map) { const clonedMap = new Map(); target.forEach((value, key) => { clonedMap.set(key, deepClone(value, map, depth - 1)); }); return clonedMap; }
// 处理 Set if (target instanceof Set) { const clonedSet = new Set(); target.forEach(value => { clonedSet.add(deepClone(value, map, depth - 1)); }); return clonedSet; }
// 处理数组和普通对象 // 克隆常规属性 Object.keys(target).forEach(key => { clone[key] = deepClone(target[key], map, depth - 1); });
// 克隆 Symbol 属性 Object.getOwnPropertySymbols(target).forEach(sym => { clone[sym] = deepClone(target[sym], map, depth - 1); });
return clone;}
使用示例
// 创建复杂对象const original = { num: 1, str: 'hello', date: new Date(), regex: /pattern/g, arr: [1, 2, { name: 'obj' }], map: new Map([['key', { value: 'map' }]]), set: new Set([1, 2, 3]), symbol: Symbol('unique'), [Symbol('private')] : 'secret', func: function() { return this.num; }};
// 创建循环引用original.self = original;original.arr.push(original);
// 执行深拷贝const cloned = deepClone(original, new WeakMap(), 5);
// 验证结果console.log(cloned !== original); // trueconsole.log(cloned.arr !== original.arr); // trueconsole.log(cloned.arr !== original.arr); // trueconsole.log(cloned.map.get('key') !== original.map.get('key')); // trueconsole.log(cloned.self === cloned); // true (循环引用保持)console.log(cloned.date instanceof Date); // trueconsole.log(cloned.func()); // 1 (函数保持)
业务场景
- 状态管理:Redux/Vuex 中 reducer 更新状态
- 数据快照:实现撤销/重做功能
- 数据隔离:防止污染原始数据(如配置对象)
- 缓存数据:存储历史数据副本
- 复杂对象传输:Web Worker 间的数据传递
相似题目
- 实现浅拷贝(简单) 核心:Object.assign / 展开运算符
- 实现对象冻结(中等) 核心:Object.freeze / Proxy 拦截
- 实现对象比较(中等) 核心:深度递归比较对象属性
- 实现 immutable 对象(困难) 核心:结构共享 + 持久化数据结构
- 实现对象属性监听(困难) 核心:Proxy 拦截属性访问/修改