标准答案
- 基础类型可以直接返回,对象和数组需要创建新的容器再递归复制属性。
- 循环引用需要用 WeakMap 记录原对象到新对象的映射。
- Date、RegExp、Map、Set 等内置对象不能简单按普通对象复制。
- 函数通常不做深拷贝,更多是保留引用,具体要看业务需求。
- 完整深拷贝还要考虑 Symbol key、不可枚举属性、原型链和属性描述符,但面试里可以先说明边界。
题目解析
深拷贝的重点不是写出一个万能函数,而是知道哪些值应该被复制成新引用,哪些值只能保留引用或需要特殊处理。普通对象和数组只是最基础的场景。
循环引用是必须单独处理的边界。对象 A 指向对象 B,对象 B 又指回 A,如果没有缓存已访问对象,递归会一直绕圈。
业务里也不应该无脑深拷贝整个大对象。它会带来性能成本,还可能破坏类实例、响应式对象、DOM 节点或带方法的数据结构。
代码示例
WeakMap 用来记录已经复制过的引用,解决循环引用。
JavaScript
function deepClone(value, cache = new WeakMap()) {
if (value === null || typeof value !== 'object') {
return value
}
if (cache.has(value)) {
return cache.get(value)
}
if (value instanceof Date) return new Date(value)
if (value instanceof RegExp) return new RegExp(value)
const result = Array.isArray(value) ? [] : {}
cache.set(value, result)
Reflect.ownKeys(value).forEach(key => {
result[key] = deepClone(value[key], cache)
})
return result
}常见误区
- 直接用 JSON.stringify / JSON.parse,当作完整深拷贝方案。它会丢失 undefined、Symbol、函数、Date、Map 等信息。
- 没有处理循环引用,复杂对象一拷贝就栈溢出。
- 忽略响应式对象或类实例,深拷贝后业务语义被破坏。