915 字
5 分钟
JavaScript Deep Clone

题目描述#

实现一个健壮的深拷贝函数,要求:

  1. 支持所有 JavaScript 数据类型(基础类型/对象/数组/Map/Set等)
  2. 处理循环引用问题(对象相互引用)
  3. 保留对象原型链关系
  4. 正确处理特殊对象(Date, RegExp 等)
  5. 处理 Symbol 类型属性
  6. 可配置克隆深度(可选)

要求完整实现深拷贝逻辑,并通过测试用例验证。

解题思路#

  1. 类型判断:区分基础类型和引用类型
  2. 特殊对象处理:Date/RegExp 等创建新实例
  3. 原型继承:使用 Object.create 保持原型链
  4. 循环引用检测:使用 WeakMap 存储已拷贝对象
  5. 递归拷贝:深度遍历对象属性
  6. 容器对象处理:Map/Set/Array 单独处理
  7. Symbol 支持:遍历 Symbol 类型属性

关键洞察#

  1. 循环引用处理:WeakMap 存储已拷贝对象防止无限递归
  2. 原型保持Object.getPrototypeOf + Object.create 保持原型链
  3. 特殊对象克隆:Date/RegExp 等需要重建实例
  4. 容器对象区分:Map/Set/Array 需要特殊处理
  5. Symbol 支持Object.getOwnPropertySymbols 获取 Symbol 属性
  6. 性能优化: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); // true
console.log(cloned.arr !== original.arr); // true
console.log(cloned.arr !== original.arr); // true
console.log(cloned.map.get('key') !== original.map.get('key')); // true
console.log(cloned.self === cloned); // true (循环引用保持)
console.log(cloned.date instanceof Date); // true
console.log(cloned.func()); // 1 (函数保持)

业务场景#

  1. 状态管理:Redux/Vuex 中 reducer 更新状态
  2. 数据快照:实现撤销/重做功能
  3. 数据隔离:防止污染原始数据(如配置对象)
  4. 缓存数据:存储历史数据副本
  5. 复杂对象传输:Web Worker 间的数据传递

相似题目#

  1. 实现浅拷贝(简单) 核心:Object.assign / 展开运算符
  2. 实现对象冻结(中等) 核心:Object.freeze / Proxy 拦截
  3. 实现对象比较(中等) 核心:深度递归比较对象属性
  4. 实现 immutable 对象(困难) 核心:结构共享 + 持久化数据结构
  5. 实现对象属性监听(困难) 核心:Proxy 拦截属性访问/修改
JavaScript Deep Clone
https://website-truelovings-projects.vercel.app/posts/code/javascript/javascript-deep-clone/
作者
欢迎来到StarSky的网站!
发布于
2025-08-17
许可协议
CC BY-NC-SA 4.0