1. 深拷贝的简单实现(数组和对象)
// 使用 WeakMap 是因为其弱引用
const deepClone = (tar, map = new WeakMap()) => {
// 判断是否为引用类型
if (typeof tar === "object" && tar !== null || typeof tar === "function") {
// 如果 tar 已经被拷贝过一份了, 则直接返回当时拷贝的值即可(防止循环引用)
if (map.has(tar)) {
return map.get(tar)
}
// 如果被克隆对象是 Array, 则返回的对象也是 Array 类型
// 如果被克隆的对象是 Object, 则返回的对象也是 Object 类型
const retObj = Array.isArray(tar) ? [] : {}
// 如果当前是 tar 的第一次拷贝, 则记录下该次拷贝(tar 与 tar的拷贝值)
map.set(tar, retObj)
for (const key in tar) {
// 防止拷贝原型上的属性
if (Reflect.hasOwnProperty.call(tar, key)) {
retObj[key] = deepClone(tar[key], map)
}
}
return retObj
} else {
// 如果不是引用类型, 则直接返回值即可
return tar
}
}
2. 关于取代深拷贝的一个想法
什么时候需要用到深拷贝?
我能想到的场景就是: 需要修改原对象的某个属性值而又不影响到原对象的时候
关于对象的深拷贝, 其有几个问题:
函数的闭包无法拷贝怎么办? 数据之间的相互引用成环怎么办?
参照 Immutable 的想法, 我们可以简单的进行如下操作:
对 A 中的 F 进行修改, 会得到一个新的对象 a, 在此过程中, 除了与 F 有关的数据发生了变化之外, 其余的数据仍是原来的数据, 相较于深拷贝, 其防止了大量无用的操作(如果 B, D, E 中还有很深的数据嵌套, 那性能损失就很大了)
const change = (tar, {path, value}) => {
const key = path[Symbol.iterator]()
const traverse = (tar, prop) => {
if (prop === undefined) return value
const retObj = Object.assign(Array.isArray(tar) ? [] : {}, tar)
retObj[prop] = traverse(tar[prop], key.next().value)
return retObj
}
return traverse(tar, key.next().value)
}
let obj = {b: 1, c: {e: {g: 2, h: 3}, f: 4}, d: 5}
let ret = change(obj, {path: ["c", "f"], value: 233})