在整理前端面试问题的时候,遇到很多要求利用js实现深层克隆要求,因此特意回去翻看了资料,并将笔记和代码整理了一下。
关于深层克隆
深层克隆可以简单理解为:可以克隆第一级属性,如果某个属性又是一个内嵌的子对象,深克隆会进入子对象中,继续克隆内嵌子对象及其内容。
实现深层克隆的思路:
1、首先对需要克隆的对象进行遍历,判断里面属性值的类型
2、属性值的类型分为两种:原始值(string,number,null,undefine,boolean)和引用值(object,array)
3、如果遍历到的属性值为原始值,则直接进行拷贝
4、如果遍历到的属性值为引用值类型,则需要在目标对象的对应属性地方建立新的引用值类型,以原始引用值类型为模板,新建立的引用值为目标重复1,2,3步骤进行拷贝,从而实现深层拷贝。
下面给出代码实现过程:(附超详细注释)
function deepClone(origin,target){
var target = target || {};
toStr = Object.prototype.toString;
arrStr = "[object.Array]";
for (var key in origin) {
// 判断是否为原型链上的值
if (origin.hasOwnProperty(key)) {
// 判断当前的属性值类型是否为引用值
if(origin[key] !== "null" && typeof(origin[key]) == "object"){
// 当前引用值类型为引用值,调用原型链上的方法判断当前的属性值是否为数组
if(toStr.call(origin[key]) == arrStr){
// 根据当前属性值为数组类型,建立目标数组
target[key] = [];
} else{
// 当前属性值为对象类型,建立目标对象
target[key] = {};
}
// 以新建立的目标类型,对原始的引用值类型属性进行递归克隆,即重复步骤1,2,3,4
deepClone(origin[key],target[key]);
}
else{
// 当前的属性值类型不为引用值类型,为原始值,直接拷贝
target[key] = origin[key];
}
}
}
}
}
运行结果:
此处使用的深层拷贝算法可以很好的实现深层克隆,包括属性值为对象类型也可以完整克隆,下面验证当修改原始对象中的值时,对应的克隆对象中的值会不会发生改变:
当修改原始对象中的值时,此时克隆对象中的值并没有发生改变。这是由于虽然克隆的对象虽然由原始值而来,但是里面的引用值类型中保存的是自己独立的指针,指向被克隆出来的新房间,当原始房间中的值发生改变时,克隆出来的值不受原始值的影响。
在这里有几个扩展点:
1、当判断对应的值是否为数组或者对象时,除了文档中用到的对象原型链上的toString方法意外,还可以使用instanceof以及constructor方法。
2、为了在代码实现过程中,提高代码可读性,用到的toStr.call(Origin[prop]==‘arrStr’),其实就是判断是否是[object Array],将原型链上的tostring方法单独提了出来。
既然聊到了深层克隆,那么关于浅层克隆,也做一个简单的分析:
浅层克隆
实现被克隆对象的一一克隆,其中对引用值类型的克隆,就相当于克隆了其引用值中所保存的地址空间,克隆对象和被克隆对象中,指针指向的是同一个地址空间,当引用值改变时,两个克隆出来的对象中的值也跟着改变
function clone(Origin,Target){
var Target = Target||{};//防止用户不传Target
for ( prop in Origin){
Target[prop]=Origin[prop]
}
return Target;
}
当修改引用值中的数据时,其对应的克隆对象中的值也跟着变化: