cloneDeep

333 阅读1分钟

深拷贝

将一个对象复制给另一个对象,复制出一个完全新的对象

Object.assign

拷贝对象,将源对象整合成新对象,当对象内也是对象时,复制引用地址

const cloneDeep = (value) => {
    if (value === null || typeof value !== 'object') {
        return value
    }

    const target = Array.isArray(value) ? [] : {}

    for (const key in value) {
        // 过滤掉原型上的属性
        if (Object.hasOwnProperty.call(obj, key)) {
            target[key] = cloneDeep(obj[key])
        }
    }

    return target
}

export default cloneDeep

上述代码为什么要判断(Object.hasOwnProperty.call(obj, key)

由于对象 obj 的原型对象是 protoObj,所以在 for-in 循环中会遍历到 protoObj.b 属性,但这个属性并不是我们想要遍历的 obj 的自身属性。

因此加上 Object.hasOwnProperty.call(obj, key) 判断,可以确保只遍历到 obj 对象自身的可枚举属性,而不是遍历到其原型链上的属性。若 hasOwnProperty() 判断为 false,则表示该属性为原型链上的属性,需要跳过。

lodash中的深拷贝

  1. 确认是不是null或不是对象
  2. 通过map或wekMap来记录是否存在循环引用
  3. 复制对象
  4. 通过处理函数来复制对象
const cloneDeep = (value, customizer) => {
    const uniqueSet = new WeakMap()
    const deepClone = (object) => {
        if (typeof customizer === 'function') {
            const newValue = customizer(object)
            if (newValue !== undefined) {
                return newValue
            }
        }

        if (object === null || typeof object !== 'object') {
            return object
        }

        if (uniqueSet.has(object)) {
            return uniqueSet.get(object)
        }

        const { constructor } = Object.getPrototypeOf(object)
        const newObject = new constructor()
        uniqueSet.set(object, newObject)

        const keys = Object.keys(object)
        for (let i = 0; i < keys.length; i++) {
            const key = keys[i]
            const value = object[key]
            newObject[key] = deepClone(value)
        }

        const symbols = Object.getOwnPropertySymbols(object)
        for (let i = 0; i < symbols.length; i++) {
            const symbol = symbols[i]
            const value = object[symbol]
            newObject[symbol] = deepClone(value)
        }

        return newObject
    }

    return deepClone(value)
}

export default cloneDeep