为什么要使用深度克隆?
直接克隆的话,克隆类中的数组只是获得了原始类中的数组指向。这样的话,原始类和克隆类中的数组指向同一个空间,当你需要操作克隆类中的数组时,原始类的数组也会跟着改变,这一点是相当不可以的,也不符合逻辑。
在克隆之前,我们得先搞清楚,我们需要克隆些什么内容。
以obj为例,我们进行分析:
var obj = {
name : "嫖老师",
age : 18,
hobbies : ['basketball', 'JavaScript', 'Vue'] ,
family : {
name : "皮皮欢乐家庭",
parents : {
father : "大皮皮",
mother : "皮皮妈"
}
}
}
我们可以直观的看出来obj中有原始值和引用值。引用值为数组和对象,其中对象中,又有原始值name和引用值parents,parents中又有原始值father、mather。说明,想要完成深度克隆,必须嵌套循环实现。我们需要一层一层的进行克隆。
首先我们使用 for in 遍历出obj中的所有元素,再加以判断。基本思想可以总结为5个过程:
1、遍历对象 for (var prop in obj)
2、判断是不是原始值 (使用typeof()判断)
3、判断是数组还是对象(使用toString()判断)
4、建立响应的数组或对象
5、递归
代码演示:
function deepClone(origin, target) { //传参 origin 被克隆的类 ,target 需要克隆的类
var target = target || {}; //当没有传target时,给创建个空类
toStr = Object.prototype.toString, //把toString方法应用出来
arrStr = "[object Array]" //先设定一个array的toString返回值,方便比较
for(var prop in origin) { //用for in 循环origin中所有的属性
if(origin.hasOwnProperty(prop)) { //判断所得的属性是否是自身的属性,不能使用原型链上的属性
if(origin[prop] !== "null" && typeof(origin[prop]) == 'object') { //为turn时,该属性为引用值。为false时,该元素为原始值,并且这个属性不能为null,为null克隆就没有意义
//走到这一步,就可以明确,只有数组和类俩种情况,在进行判断
if(toStr.call(origin[prop]) == arrStr) { //判断是不是数组
target[prop] = []; //是数组创建一个空数组
}else {
target[prop] = {}; //不是数组,就只能是类了
}
//在一个类中,也可能有原始值、数组、类三种情况所有,我们需要再次对这个类再进行循环
//此时,循环的出口就是,当该属性为原始值,所有以当前的prop再次调用循环,就可以实现循环递归
deepClone(origin[prop],target[prop]);
}else {
target[prop] = origin[prop]; //直接赋值
}
}
}
}
运行结果演示:
给obj1.hobbies数组push一个数据,看看obj中的hobbies是否有影响
深度克隆成功,俩个hobbies的空间指向并不是同一个,达成了我们的需求。