深浅拷贝

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

不应用第三方库实现

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

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的源码