外观
怎么写深拷贝?
深拷贝是新建一份数据,改副本不会影响原对象。日常优先用 structuredClone。JSON.parse(JSON.stringify()) 只能处理纯数据。手写时用 WeakMap 记住已经拷过的对象,才能对付循环引用。
原理
JSON 方案会丢掉 undefined、函数、symbol,把 Date 变成字符串,把 NaN 和 Infinity 变成 null,遇到循环引用直接抛错。BigInt 也会抛错。它适合配置、接口缓存这种纯 JSON。
structuredClone 支持循环引用、Date、Map、Set、ArrayBuffer 和更多内建对象,不支持函数和 DOM 节点。浏览器和当前的 Node 都有。
手写版本按类型分发,并在往下走之前把「原对象 → 副本」放进 WeakMap:
js
function deepClone(value, seen = new WeakMap()) {
if (typeof value !== "object" || value === null) return value
if (seen.has(value)) return seen.get(value)
if (value instanceof Date) return new Date(value.getTime())
if (value instanceof RegExp) return new RegExp(value.source, value.flags)
if (value instanceof Map) {
const copy = new Map()
seen.set(value, copy)
value.forEach((item, key) => {
copy.set(deepClone(key, seen), deepClone(item, seen))
})
return copy
}
if (value instanceof Set) {
const copy = new Set()
seen.set(value, copy)
value.forEach((item) => copy.add(deepClone(item, seen)))
return copy
}
const copy = Array.isArray(value) ? [] : Object.create(Object.getPrototypeOf(value))
seen.set(value, copy)
for (const key of Reflect.ownKeys(value)) {
copy[key] = deepClone(value[key], seen)
}
return copy
}WeakMap 的键是对象,键不再被别人引用时,这条记录可以一起回收,不会把所有拷贝过的对象永远留住。
追问
浅拷贝和深拷贝的界限?
浅拷贝只复制第一层。{ ...obj } 和 Object.assign 里的对象属性仍然和原对象共享。改 copy.user.name 会改到原来的 user。
** getter 怎么办?**
上面的实现在读取时执行 getter,副本上保存的是当时的值。要连访问器一起复制,就要拷贝属性描述符,并约定 getter 里不会依赖外部状态。面试里先说明自己拷的是值。
易错点
- 先递归再记入
WeakMap,循环引用会栈溢出。必须先登记副本。 typeof null是"object",要单独排除。- 函数、DOM 节点没有通用的深拷贝语义。碰到它们要明确拒绝或原样返回,不要假装拷贝成功。