如果处理简单对象,可以使用 JSON。
如果不考虑兼容性,可以使用 structuredClone。
要点
- 循环引用
- 不同类型处理
代码
const argsTag = '[object Arguments]';
const arrayTag = '[object Array]';
const boolTag = '[object Boolean]';
const dateTag = '[object Date]';
const errorTag = '[object Error]';
const mapTag = '[object Map]';
const numberTag = '[object Number]';
const objectTag = '[object Object]';
const regexpTag = '[object RegExp]';
const setTag = '[object Set]';
const stringTag = '[object String]';
const symbolTag = '[object Symbol]';
const weakMapTag = '[object WeakMap]';
function _cloneDeep(value: any, stack = new Map()) {
let result: any;
// 原始类型直接返回
if (!(value !== null && (typeof value === 'object' || typeof value === 'function'))) {
return value;
}
// 处理循环引用
if ([arrayTag, mapTag, objectTag, setTag].includes(Object.prototype.toString.call(value))) {
if (stack.has(value)) {
return stack.get(value);
}
stack.set(value, result);
}
switch (Object.prototype.toString.call(value)) {
case boolTag:
case dateTag:
case numberTag:
case stringTag:
result = new value.constructor(value);
break;
case regexpTag:
result = new value.constructor(value.source, value.flags);
result.lastIndex = value.lastIndex;
break;
case mapTag:
result = new value.constructor();
(value as Map<any, any>).forEach((v, k) => {
(result as Map<any, any>).set(k, _cloneDeep(v, stack));
});
break;
case setTag:
result = new value.constructor();
(value as Set<any>).forEach((v) => {
(result as Set<any>).add(_cloneDeep(v, stack));
});
break;
case arrayTag:
result = new value.constructor();
for (let index = 0; index < value.length; index++) {
result[index] = _cloneDeep(value[index], stack);
}
break;
case objectTag:
result = new value.constructor();
Object.keys(value).forEach((k) => {
result[k] = _cloneDeep(value[k], stack);
});
break;
default:
result = value;
break;
}
return result;
}
function cloneDeep<T>(value: T): T {
return _cloneDeep(value);
}
补充
lodash 实现了克隆 Symbol,我个人觉得 Symbol 代表独一无二的,不应该被克隆,就像函数一样直接返回。