关于javascript:实现深拷贝

30次阅读

共计 2581 个字符,预计需要花费 7 分钟才能阅读完成。

深浅拷贝

浅拷贝: 仅仅是复制了援用(地址)。即复制了之后,原来的变量和新的变量指向同一个货色,彼此之间的操作会相互影响。
深拷贝: 在堆中从新分配内存,领有不同的地址,然而值是一样的,复制后的对象与原来的对象是齐全隔离,互不影响。
深浅拷贝的次要 区别 就是:复制的是援用 (地址) 还是复制的是实例。

不应用第三方库实现

比方当初咱们要对上面这个对象做深拷贝,在不应用第三方库的状况下,该如何实现呢。

const source = {
    field1: 1,
    field2: undefined,
    field3: 'hello',
    field4: null,
    field4: {
        child: 'xiaoming',
        child2: {child2: 'ahong'}
    },
    fieldArray: [1, 2, 3, 4]
}

obj -> JsonString -> newObj

JSON.parse(JSON.stringify(source));

入手实现

  • 原始数据类型,间接复制
  • 援用类型,创立一个新对象,对各属性深拷贝 赋值给新对象

秉着上述两个方向,第一个简略的的深拷贝就实现了。
1.1

function clone2(target){if(typeof target == 'object' && target != null){let cloneTarget = Array.isArray(target) ? [] : {}
        for(let key in target){cloneTarget[key] = clone2(target[key])
        }
        return cloneTarget
    }else{return target}
}
这种实现如果呈现了援用了自身的状况,source.XXX = source 栈溢出。

1.2
如何优化呢,开拓一块新的存储空间,来存储以后对象和拷贝对象的对应关系,当须要拷贝以后对象时,先去存储空间中找,有没有拷贝过这个对象。

function clone3(target, targetMap = new Map()){if(typeof target == 'object' && target != null){let cloneTarget = Array.isArray(target) ? [] : {}
        if(targetMap.get(target)){return target}
        targetMap.set(target, cloneTarget)
        for(let key in target){cloneTarget[key] = clone3(target[key], targetMap)
        }
        return cloneTarget
    }else{return target}
}

Map 为强援用,如果这个援用的层级很高,垃圾回收机制不会被动帮咱们回收,可能会呈现性能问题。那么能够思考把 Map 替换为 WeakMap, WeakMap 为 ES6 提供的原生数据结构,只有对象的其余援用被删除,垃圾回收机制就会开释该对象占用的内存,从而防止内存透露。

function clone4(target, targetMap = new WeakMap()){if(typeof target == 'object' && target!=null){let cloneTarget = Array.isArray(target) ? [] : {}
        if(targetMap.get(target)){return target}
        targetMap.set(target, cloneTarget)
        for(let key in target){cloneTarget[key] = clone4(target[key], targetMap)
        }
        return cloneTarget
    }else{return target}
}

1.3
家喻户晓,for in 的速度是慢于 for 循环和 while 的,那么革新一下循环。

function clone5(target, targetMap = new WeakMap()){if(typeof target == 'object'  && target!=null){let cloneTarget = Array.isArray(target) ? [] : {}
        if(targetMap.get(target)){return target}
        targetMap.set(target, cloneTarget)
        let keys = Array.isArray(target) ? null : Object.keys(target)
        let length = (keys || target).length
        let index = -1
        while(++index < length){
            let key = index
            if(keys){key = (keys || target)[index]
            }
            cloneTarget[key] = clone5(target[key], targetMap)
        }
        return cloneTarget
    }else{return target}
}

将 while 循环抽离进去

function customForEach(array, iteratee) {
    let index = -1
    while(++index < array.length){iteratee(array[index], index)
    }
    return array
}

function clone6(target, targetMap = new WeakMap()){if(typeof target == 'object' && target!=null){let cloneTarget = Array.isArray(target) ? [] : {}
        if(targetMap.get(target)){return target}
        targetMap.set(target, cloneTarget)
        let keys = Array.isArray(target) ? null : Object.keys(target) 
        customForEach(keys || target, (value, key) => {if(keys){key = value}
            cloneTarget[key] = clone6(target[key], targetMap)
        })
        return cloneTarget
    }else{return target}
}

这只是简易版的,适宜大部分业务数据的深拷贝,还未思考到类型为 function 等状况。具体的可见原文,或者看下 lodash 的源码

正文完
 0