实现一个深拷贝,是前端经常遇到的一个问题。不论是在时间中,还是在面试中。。。 下面是两种比较好的实现深拷贝的方式。 实现方案一:
function clone(target, map = new WeakMap()) {
// 克隆原始类型
if (!isObject(target)) {
return target
}
// 初始化
const type = getType(target)
let cloneTarget
if (deepTag.includes(type)) {
cloneTarget = getInit(target, type)
} else {
return cloneOtherType(target, type)
}
// 防止循环引用
if (map.get(target)) {
return map.get(target)
}
map.set(target, cloneTarget)
// 克隆set
if (type === setTag) {
target.forEach((value) => {
cloneTarget.add(clone(value, map))
})
return cloneTarget
}
// 克隆map
if (type === mapTag) {
target.forEach((value, key) => {
cloneTarget.set(key, clone(value, map))
})
return cloneTarget
}
// 克隆对象和数组
const keys = type === arrayTag ? undefined : Object.keys(target)
forEach(keys || target, (value, key) => {
if (keys) {
key = value
}
cloneTarget[key] = clone(target[key], map)
})
return cloneTarget
}
module.exports = {
clone,
}
上面的代码,需要学到: 基本实现: 递归能力 循环引用: 考虑问题的全面性 理解weakmap的真正意义 多种类型: 考虑问题的严谨性 创建各种引用类型的方法,JS API的熟练程度 准确的判断数据类型,对数据类型的理解程度 通用遍历: 写代码可以考虑性能优化 了解集中遍历的效率 代码抽象能力 拷贝函数: 箭头函数和普通函数的区别 正则表达式熟练程度 感谢ConardLi的文章:(如何写出一个惊艳面试官的深拷贝)[cloud.tencent.com/developer/a…]
实现方案二:
function deepClone(origin, target, hash = new WeakMap()) {
// origin:要被拷贝的对象
// 需要完善,克隆的结果和之前保持相同的所属类
var target = target || {}
// 处理特殊情况
if (origin == null) return origin // null 和 undefined 都不用处理
if (origin instanceof Date) return new Date(origin)
if (origin instanceof RegExp) return new RegExp(origin)
if (typeof origin !== "object") return origin // 普通常量直接返回
// 防止对象中的循环引用爆栈,把拷贝过的对象直接返还即可
if (hash.has(origin)) return hash.get(origin)
hash.set(origin, target) // 制作一个映射表
// 拿出所有属性,包括可枚举的和不可枚举的,但不能拿到symbol类型
var props = Object.getOwnPropertyNames(origin)
props.forEach((prop, index) => {
if (origin.hasOwnProperty(prop)) {
if (typeof origin[prop] === "object") {
if (Object.prototype.toString.call(origin[prop]) == "[object Array]") {
// 数组
target[prop] = []
deepClone(origin[prop], target[prop], hash)
} else if (
Object.prototype.toString.call(origin[prop]) == "[object Object]"
) {
// 普通对象
target[prop] = {}
deepClone(origin[prop], target[prop], hash)
} else if (origin[prop] instanceof Date) {
// 处理日期对象
target[prop] = new Date(origin[prop])
} else if (origin[prop] instanceof RegExp) {
// 处理正则对象
target[prop] = new RegExp(origin[prop])
} else {
// null
target[prop] = null
}
} else if (typeof origin[prop] === "function") {
var _copyFn = function (fn) {
var result = new Function("return " + fn)()
for (var i in fn) {
deepClone[(fn[i], result[i], hash)]
}
return result
}
target[prop] = _copyFn(origin[prop])
} else {
// 除了object、function,剩下都是直接赋值的原始值
target[prop] = origin[prop]
}
}
})
// 单独处理symbol
var symKeys = Object.getOwnPropertySymbols(origin)
if (symKeys.length) {
symKeys.forEach((symKey) => {
target[symKey] = origin[symKey]
})
}
return target
}
// 测试代码
let s1 = Symbol("s1")
let obj = {
a: "100",
b: undefined,
c: null,
d: Symbol(2),
e: /^\d+$/,
f: new Date(),
g: true,
arr: [10, 20, 30],
school: {
name: "cherry",
[s1]: "s1",
},
fn: function fn() {
console.log("fn")
},
}
obj.h = obj
let obj2 = deepClone(obj)
console.log(obj, obj2)
感谢作者LBJ, 文章参考: (一篇彻底搞定对象的深度克隆 | 包括function和symbol类型)[juejin.cn/post/697441…]