直接上代码,注释中有说明
const isObject = val => val !== null && typeof val === 'object';
const isFunction = val => typeof val === 'function';
const isSymbol = val => typeof val === 'symbol';
const isArray = Array.isArray;
const isError = val => val instanceof Error;
export function deepClone(original, cacheMap = new WeakMap()) {
if (isFunction(original)) return original;
if (isSymbol(original)) return Symbol(original.description);
if (isError(original)) return original;
if (!isObject(original)) return original;
const types = [RegExp, Date, Map, WeakMap, Set, WeakSet];
if (types.some(type => original instanceof type)) {
const constr = original.constructor;
if (constr && typeof constr === 'function') {
return new constr(original);
}
}
if (cacheMap.has(original)) return cacheMap.get(original);
const newObj = isArray(original) ? [] : {};
cacheMap.set(original, newObj);
const keys = Reflect.ownKeys(original);
for (const key of keys) {
newObj[key] = deepClone(original[key], cacheMap);
}
Object.setPrototypeOf(newObj, Object.getPrototypeOf(original));
return newObj;
}
再来个看着很乱的测试用例
function testDeepClone() {
function fn() {
return 'fn';
}
let e = new Error('错了');
let reg = /d/g;
const symbolkey = Symbol('作为 key 值');
class MySet extends Set {
mySet: string;
constructor(...args) {
super(...args);
this.mySet = 'mySet';
}
}
const prototpyeObj = {};
Object.setPrototypeOf(prototpyeObj, { super: '原型上的值', fn() {} });
const obj = {
num: 123,
string: 'string',
boolean: true,
null: null,
undefined: undefined,
object: {},
array: [],
reg: reg,
date: new Date(),
Symbol: Symbol(''),
[symbolkey]: 'symbol 作为 key 值',
set: new Set([123, 456, 789]),
mySet: new MySet([111, 222, 333]),
map: new Map([
[123, 123],
[456, 456],
[789, 789],
]),
prototpyeObj,
arr: [
'number: ',
123,
'string: ',
'string',
'boolean: ',
true,
'null: ',
null,
'undefined: ',
undefined,
'object: ',
{},
'array: ',
[],
'reg: ',
reg,
new Date(),
'Symbol: ',
Symbol(''),
'symbolkey: ',
symbolkey,
'set: ',
new Set([123, 456, 789]),
new MySet([111, 222, 333]),
'map: ',
new Map([
[123, 123],
[456, 456],
[789, 789],
]),
'fn: ',
fn,
'e: ',
e,
'error: ',
Error('我错了'),
],
fn: fn,
e: e,
error: Error('我错了'),
};
const newObj = deepClone(obj);
console.log('obj --------------------------->>>', obj);
console.log('newObj ------------------------>>>', newObj);
console.log('obj === newObj ---------------->>>', obj === newObj);
console.log('obj.object === newObj.object--->>>', obj.object === newObj.object);
console.log('obj.array === newObj.array----->>>', obj.array === newObj.array);
console.log('obj.reg === newObj.reg--------->>>', obj.reg === newObj.reg);
console.log('obj.Symbol === newObj.Symbol--->>>', obj.Symbol === newObj.Symbol);
console.log('obj.set === newObj.set--------->>>', obj.set === newObj.set);
console.log('obj.mySet === newObj.mySet----->>>', obj.mySet === newObj.mySet);
console.log('obj.map === newObj.map--------->>>', obj.map === newObj.map);
newObj.set.add(110);
newObj.mySet.add(444);
newObj.map.set(110, 110);
console.log('Function 和 Error 直接使用同一个值-------------------------------');
console.log('obj.fn === newObj.fn----------->>>', obj.fn === newObj.fn);
console.log('obj.e === newObj.e------------->>>', obj.e === newObj.e);
console.log('obj.error === newObj.error----->>>', obj.error === newObj.error);
console.log('对象原型的属性和方法------------->>>', obj.prototpyeObj.super);
let loopObj = { a: 123 };
loopObj.loopObj = loopObj;
const newLoopObj = deepClone(loopObj);
console.log('循环引用 ------------->>>', newLoopObj);
console.log('循环引用 ------------->>>', newLoopObj === newLoopObj.loopObj);
console.log('循环引用 ------------->>>', newLoopObj.loopObj === newLoopObj.loopObj.loopObj);
console.log('循环引用 ------------->>>', newLoopObj.loopObj.loopObj === newLoopObj.loopObj.loopObj.loopObj);
}
testDeepClone();