尽管通过newObj = [...obj] 创建了一个新的数组,但是数组中的元素如果是基本类型,则新数组会拥有这些元素的独立副本;如果数组元素是引用类型,新数组则会复制这些引用(内存地址),而不是复制对象本身。所以仍需要对数组继续遍历递归。
const deepClone = (obj, hash = new WeakMap()) => {
if (obj == null) return obj; //如果是null或者undefined直接返回,由于typeof null === "object",所以需要单独判断。
if (typeof obj !== "object") return obj; //如果不是对象类型无需深拷贝,直接返回
if (obj instanceof Date) return new Date(obj); //如果是Date类型,返回一个新的Date对象
if (obj instanceof RegExp) return new RegExp(obj); //如果是RegExp类型,返回一个新的RegExp对象
if (typeof obj === "function") return obj;
let newObj = Object.create(null); //初始化新对象
if (Array.isArray(obj)) {
//如果obj是数组,则使用扩展运算符构造一个新数组。
newObj = [...obj];
hash.set(obj, newObj); //使用obj为键存入map中
for (let i = 0; i < obj.length; i++) newObj[i] = deepClone(obj[i], hash); //然后递归遍历数组每一项,对每个元素深拷贝
return newObj;
}
newObj = Object.create(Object.getPrototypeOf(obj)); //如果obj为对象类型,则创建一个继承obj原型链的新对象
if (hash.get(obj)) return hash.get(obj); //如果map中已有拷贝完的对象,直接返回,无需重复深拷贝。
hash.set(obj, newObj);
for (const key of Object.keys(obj)) {
//遍历obj的每一个键
newObj[key] = deepClone(obj[key], hash); //对newObj的每一个值进行递归深拷贝
}
return newObj;
};
let bbb = [
1,
2,
{
a: 1,
b: { f: { g: 1 } },
c: [1, 2, 3],
},
];
let b = deepClone(bbb);
b[0] = 2;
b[2]["a"] = 2;
console.log(b);
console.log(bbb);
可以看到修改深拷贝的b对象并不影响原对象bbb。