深拷贝
先理解原理和示例,再练习完整口述与追问。
回答要点
支持范围、循环、共享引用、特殊对象、属性描述符。
原理拆解
深拷贝可以把输入对象图映射为新的对象图。先判断原始值,再处理支持的类型,创建目标后立刻把原对象→目标登记到缓存,之后递归属性,才能处理环和保留共享引用关系。支持范围比“递归复制所有值”更重要。
示例与推演
原对象 a.self=a,复制后应有 b.self===b 且 b!==a;a.left 与 a.right 指向同一子对象,复制后两条边仍应指向同一个新子对象。再测 Date、RegExp、Map、Set 或代码明确支持的类型,函数、访问器和原型按声明范围处理。
易错点
不要在访问每个键时无意执行具有副作用的 getter 并声称保留描述符。DOM 与函数通常不能通用复制,复制大型数据也有成本。
参考回答可直接练习口述
我先限定数据范围,再用 WeakMap 记录“原对象 → 新对象”,既处理循环,也保持同一对象被多处引用的关系。下面支持同一 realm 的普通对象、数组、Date、RegExp、Map、Set,以及它们的数据属性;复制 Symbol 和不可枚举数据属性。函数、访问器和其他实例明确抛错,不假装万能复制器。不会调用原对象的 getter,也不复制任意对象内部槽或外部资源。
function deepClone(value, seen = new WeakMap()) {
if (typeof value === 'function') throw new TypeError('不支持函数')
if (value === null || typeof value !== 'object') return value
if (seen.has(value)) return seen.get(value)
let copy
if (Array.isArray(value)) copy = new Array(value.length)
else if (value instanceof Date) copy = new Date(value.getTime())
else if (value instanceof RegExp) copy = new RegExp(value.source, value.flags)
else if (value instanceof Map) copy = new Map()
else if (value instanceof Set) copy = new Set()
else {
const prototype = Object.getPrototypeOf(value)
if (prototype !== Object.prototype && prototype !== null) {
throw new TypeError('不支持该对象类型')
}
copy = Object.create(prototype)
}
seen.set(value, copy)
if (value instanceof Map) {
for (const [key, item] of value) {
copy.set(deepClone(key, seen), deepClone(item, seen))
}
}
if (value instanceof Set) {
for (const item of value) copy.add(deepClone(item, seen))
}
for (const key of Reflect.ownKeys(value)) {
const descriptor = Object.getOwnPropertyDescriptor(value, key)
if (!Object.prototype.hasOwnProperty.call(descriptor, 'value')) {
throw new TypeError('不支持访问器属性')
}
Object.defineProperty(copy, key, {
...descriptor,
value: deepClone(descriptor.value, seen)
})
}
return copy
}
**示例:**原对象 a.self=a,克隆后 b!==a 且 b.self===b。若原对象两属性共用一对象,克隆后仍共用一个新对象。Map 的对象键也被复制,不能再用旧键查新 Map。额外空间 O(对象图大小),递归栈与嵌套深度相关,极深数据可能溢出;不保留 extensible/frozen 状态,不支持 DOM、WeakMap、Promise、TypedArray 和任意类实例;内建对象的子类按相应内建类型复制,不保留子类语义。对 Proxy 访问也可能触发其陷阱,本实现不隔离恶意代理。