要实现一个深度克隆,我们先了解JavaScript中数据类型:
- 原始类型:Undefined,Null,Boolean,Number,String,Symbol
- 引用类型:Object
浅克隆之所以被称为浅克隆,是因为对象只会被克隆最外部的一层,至于更为深层的对象,则依然是通过引用指向同一块堆内存
// 浅克隆函数
function shallowClone(o) {
const obj = {};
for ( let i in o) {
obj[i] = o[i];
}
return obj;
}
// 被克隆对象
const oldObj = {
a: 1,
b: [ 'e', 'f', 'g' ],
c: { h: { i: 2 } }
};
const newObj = shallowClone(oldObj);
console.log(newObj.c.h, oldObj.c.h); // { i: 2 } { i: 2 }
console.log(oldObj.c.h === newObj.c.h); // true
Object.assign()可以实现同样的功能
const newObj = JSON.parse(JSON.stringify(oldObj));
问题:
- 无法实现对函数,RegExp,undefined,symbol等特殊值的克隆
- 会抛弃对象的constructor,所有的构造函数会指向Oject
- 对象有循环引用,会报错
var obj = {
a: 10,
b: 20,
omg: {
name: 'xuguojun',
sex: 'male'
}
}
function deepcopy(obj){
var newobj = {};
for(arr in obj){
if (typeof obj[arr]==='object' && obj[arr] !== null) {
newobj[arr] = deepcopy(obj[arr]); //递归,核心代码
} else {
newobj[arr] = obj[arr];
}
}
return newobj;
}
obj2 = deepcopy(obj);
console.log(obj2);
obj2.omg.name = 'PDD';
console.log(obj2.omg.name);
console.log(obj.omg.name);
当然,我们这个深克隆还不算完美,例如Buffer对象、Promise、Set、Map可能都需要我们做特殊处理,另外对于确保没有循环引用的对象,我们可以省去对循环引用的特殊处理,因为这很消耗时间,不过一个基本的深克隆函数我们已经实现了
在生产环境中最好用[lodash]
[https://github.com/lodash/lodash]的深克隆实现.