网上有太多的深度克隆版本了,看得眼花缭乱。如何才能清晰的给面试官留下一个完美印象的版本。我想这一个案例可以一试
const target = {
field1: 1,
field2: null,
field3: {
child: 'child'
},
field4: [2, 4, 8],
field5: new Set([1, '123', null, {name: 123}, undefined]),
field6: new Map([["name", {name: 123}]]),
say() {
console.log(this.name)
},
field7: new Date("2001-12-1"),
field8: new RegExp(/123/),
};
// 模拟一下有循环引用的使用的情况
target.target = target
// 判断一下是否为基础类型,或者应用类型
function isNormal(target) {
if (target === null) {
return true
}
return typeof target !== "object"
}
// 针对不同的数据类型,应该有不同的克隆方法
function cloneMap(target, set) {
let map = new Map();
for (let [key, val] of target.entries()) {
map.set(key, deepClone(val, set))
}
return map
}
function cloneSet(target, set) {
let mySet = new Set();
for (let val of target) {
mySet.add(deepClone(val, set))
}
return mySet
}
function cloneArray(target, set) {
let arr = [];
for (let val of target) {
arr.push(deepClone(val, set))
}
return arr
}
function cloneObject(target, set) {
let res = {};
for (let key in target) {
res[key] = deepClone(target[key], set)
}
return res
}
function cloneDate(target, set) {
return new Date(target)
}
function cloneRegExp(target, set) {
return new RegExp(target)
}
// set数据结构用来保存已经克隆过的对象,如果已经克隆过,则直接从set中去除克隆后的对象
function deepClone(target, set = new Set()) {
if (isNormal(target)) {
return target
}
if (set.has(target)) {
return target
}
set.add(target)
// 更具引用数据的类型,采用不同的克隆方法,而且避免修改了原型链,我们直接用最原始的方法判断数据类型
switch (Object.prototype.toString.call(target)) {
// 这里需要对所有不同类型的引用类型进行判断。如果所有特殊案例都不符合,则用基本的对象类型。
case "[object Map]":
return cloneMap(target, set);
case "[object Set]":
return cloneSet(target, set)
case "[object Array]":
return cloneArray(target, set)
case "[object Date]":
return cloneDate(target, set)
case "[object RegExp]":
return cloneRegExp(target, set)
default:
return cloneObject(target, set)
}
}
console.log(deepClone(target))
console.log(new Map().toString())