谈谈JS浅深克隆以及如何实现

306 阅读2分钟

所谓克隆就是复制,正常来说,复制出来的不应该会影响原来的,但事实并非如此,从而有了深浅克隆之分。直接上代码论(方便大家方便看,所以截图这块)

image.png

可以看到:浅克隆只是克隆了第一层,cloneObj和 obj中的 o对象仍然是指向同一个堆内存,因此,对cloneObj.o.x赋新值时, obj.o.x的值也随之改变。

如果是深克隆 无论cloneObj怎么改变,都不会影响obj的值

除了利用扩展运算符实现浅克隆,还有:

Object.assign()

const obj1 = {a: 1, b: 2}

const obj2 = Object.assign({}, obj1) 

for...in

let obj1 = {
    name: "bob",
    age: 18,
    sex: "man",
    o: { x: 1 } 
} 
let obj2 = {}

for(let key in obj1){
  // fon..in会遍历原型上的方法,因此要阻止遍历原型
  if(!obj1.hasOwnProperty(key)) break;
  
  // 将 obj1 的所有属性遍历并赋值给 obj2
  obj2[key] = obj1[key]
}

Object.keys()

let obj1 = {
    name: "bob",
    age: 18,
    sex: "man",
    o: { x: 1 } 
}
let obj2 = {}

// 与for...in 不同的是,Object.keys()不会遍历原型上的方法
for (let key Object.keys(obj1) {
   obj2[key] = obj1[key]
}

实现深克隆

如果对象中的属性值不是函数、undefined以及symbol值等

一般使用JSON.stringify 和 JSON.parse 简单粗暴

let obj1 = {
    name: "bob",
    age: 18,
    sex: "man",
    o: { x: 1 } 
}
let obj2 = JSON.parse(JSON.stringify(obj1))
obj2.o.x = 2
console.log(obj1.o.x) // 不受Obj2的影响,仍然是 1 

当然,要实现特殊值的深克隆,需要特殊处理

递归方法实现深克隆

const cloneDeep = (target, hash = new WeakMap()) => {
    // 如果是基本类型直接返回
    if(typeof target !== 'object' || target === null) {
        return target
    }
    // 如果是正则
    if(target instanceof RegExp) {
        return new RegExp(target)
    }
    // 如果是时间对象
    if (target instanceof Date) {
        return new Date(target)
    }
    // 哈希表中存在直接返回
    if (hash.has(target)) return hash.get(target)
    
    const cloneTarget = Array.isArray(target) ? [] : {}
    hash.set(target, cloneTarget)
    
    // 对Symbol属性处理
    const symKeys = Object.getOwnPropertySymbols(target)
    if (symKeys.length) {
        symKeys.forEach(sk => {
            // 处理symbol的值为对象且非空对象
            if (typeof target[sk] === 'object' && target[sk] !== null) {
                cloneTarget[sk] = cloneDeep(target[sk])
            } else {
                cloneTarget[sk] = target[sk]
            }
        })
    }
    
    // 遍历target, 且不遍历原型上的属性(使用ES6的方法,不使用for...in)
    for (let key of Object.keys(target)) {
        cloneTarget[key] = typeof target[key] === 'object' && target[key] !== null 
        ? cloneDeep(target[key], hash) : target[key]
    }  
    return cloneTarget
}
验证:

image.png

可以看到,深克隆已实现,并且克隆对象clone改变并不会影响 原对象obj

最后,实现深克隆的方法还有很多,有兴趣的朋友的细究