深浅拷贝
浅拷贝: 仅仅是复制了援用(地址)。即复制了之后,原来的变量和新的变量指向同一个货色,彼此之间的操作会相互影响 。
深拷贝: 在堆中从新分配内存,领有不同的地址,然而值是一样的,复制后的对象与原来的对象是齐全隔离,互不影响。
深浅拷贝的次要区别就是:复制的是援用(地址)还是复制的是实例。
不应用第三方库实现
比方当初咱们要对上面这个对象做深拷贝,在不应用第三方库的状况下,该如何实现呢。
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的源码