深拷贝我们知道是引用值的一个问题,因为在拷贝的时候,拷贝的是在内存中同一个引用。所以当其中的一个应用值发生改变的时候,其他的同一个引用值也会发生变化。那么针对于这种情况,我们需要进行深度拷贝,这样就可以做到引用值之间互不干扰的情况。
ES5 深拷贝
function deepClone(origin, target){
var target = target || {},
toStr = Object.prototype.toString,
arrType = '[object Array]';
for (const key in origin) {
if (origin.hasOwnProperty.call(origin, key)) {
if(typeof origin[key] == 'object' && origin[key] !== null){
target[key] = toStr.call(origin[key]) === arrType ? [] : {};
deepClone(origin[key],target[key])
}else{
target[key] = origin[key]
}
}
}
return target;
}
ES6深拷贝
探究ES6深拷贝的之前,我们先看一下ES6中WeakMap
是什么东西?在学习ES6知识中,我们知道Map
是解决对象属性只能够是字符串的形式,在ES6中Map
的出现,让对象的属性可以是任何类型。而WeakMap
与Map
的主要区别在于前者是弱引用,后者是强引用。并且WeakMap
的键名只能够是对象的形式。
那什么是弱引用呢?我们这里弱引用指代的是WeakMap
的键名,WeakMap
它的键名所引用的对象都是弱引用,即垃圾回收机制不将该引用考虑在内,因此,只要所引用的对象的其他引用都被清除,垃圾回收机制就会释放对该对象所占用的内存。也就是说,一旦不需要,WeakMap
里面的键名对象和所对应的键值对就会自动的消失,不用手动删除引用。
下面的例子中,oBtn1oBtn2作为WeakMap
储存的两个对象,当外界将oBtn1oBtn2删除之后,那么由于WeakMap
中的键名是弱引用的原因,所以导致WeakMap
里面的键名对象和所对应的键值对就会自动的消失,不用手动的删除。这也是比较合适WeakMap
的使用场景。
const oBtn1 = document.querySelector('#btn1');
const oBtn2 = document.querySelector('#btn2');
const oBtnMap = new WeakMap();
// WeakMap
oBtnMap.set(oBtn1, handleBtn1Click);
oBtnMap.set(oBtn2, handleBtn2Click);
oBtn1.addEventListener('click', oBtnMap.get(oBtn1), false);
oBtn2.addEventListener('click', oBtnMap.get(oBtn2), false);
function handleBtn1Click(){}
function handleBtn2Click(){}
// 删除节点
oBtn1.remove();
oBtn2.remove();
熟悉WeakMap
之后,我们来看ES6深拷贝问题。其实ES6深拷贝面临的问题是引用的循环,我们先来看一个例子。从例子的现象来看,此时由于引用值的相互引用问题,导致test1test2相互引用,无限的引用下去。如果利用上面的ES5的深拷贝方式,那么就会抛出异常,所以我们尝试利用ES6的方式来更好的解决下面的这种问题。
const test1 = {};
const test2 = {};
test1.test2 = test2;
test2.test1 = test1;
console.log(test1);
function deepClone(origin, hashMap = new WeakMap()) {
if (origin == null || typeof (origin) !== 'object') {
return origin;
}
if (origin instanceof Date) {
return new Date(origin);
}
if (origin instanceof RegExp) {
return new RegExp(origin);
}
const hashKey = hashMap.get(origin);
if (hashKey) {
return hashKey;
}
const target = new origin.constructor();
hashMap.set(origin, target);
for (var key in origin) {
if (origin.hasOwnProperty(key)) {
target[key] = deepClone(origin[key], hashMap);
}
}
return target;
}