Skip to main content

deep-copy

function deepClone(value, hash = new WeakMap()/*原对象无其他引用时
key 可被 GC 回收,条目自动消失*/) {
// 基本类型 & null & undefined
if (value === null || typeof value !== 'object') {
return value;
}

// 处理循环引用
if (hash.has(value)) {
return hash.get(value);
}

// 特殊对象:Date
if (value instanceof Date) {
return new Date(value);
}

// 特殊对象:RegExp
if (value instanceof RegExp) {
return new RegExp(value.source, value.flags);
}

// 特殊对象:Map
if (value instanceof Map) {
const cloneMap = new Map();
hash.set(value, cloneMap);
for (const [k, v] of value.entries()) {
cloneMap.set(deepClone(k, hash), deepClone(v, hash));
}
return cloneMap;
}

// 特殊对象:Set
if (value instanceof Set) {
const cloneSet = new Set();
hash.set(value, cloneSet);
for (const item of value) {
cloneSet.add(deepClone(item, hash));
}
return cloneSet;
}

// 函数:一般保持引用(无法真正克隆闭包上下文)
if (typeof value === 'function') {
// 可选:尝试通过 toString 重建(有风险)
// return new Function('return ' + value.toString())();
return value; // 直接共享引用,更安全
}

// 数组
if (Array.isArray(value)) {
const arrCopy = [];
hash.set(value, arrCopy);
for (let i = 0; i < value.length; i++) {
arrCopy[i] = deepClone(value[i], hash);
}

// 补:数组上的 Symbol 等非下标属性
for (const sym of Object.getOwnPropertySymbols(value)) {
arrCopy[sym] = deepClone(value[sym], hash);
}

return arrCopy;
}

// 普通对象(保留原型链)
const proto = Object.getPrototypeOf(value);
const objCopy = Object.create(proto);
hash.set(value, objCopy);

// 拷贝所有自有属性(包括不可枚举属性?根据需求,一般拷贝可枚举属性即可)
// 下面拷贝所有自有属性(包括 Symbol 和可枚举 / 不可枚举)
const allProps = [
...Object.getOwnPropertyNames(value),
...Object.getOwnPropertySymbols(value)
];
for (const key of allProps) {
const descriptor = Object.getOwnPropertyDescriptor(value, key);
if (descriptor && typeof descriptor.value !== 'undefined') {
// 数据属性:递归克隆值
objCopy[key] = deepClone(descriptor.value, hash);
} else if (descriptor && (descriptor.get || descriptor.set)) {
// 访问器属性:直接复制
Object.defineProperty(objCopy, key, descriptor);
}
}

return objCopy;
}