深拷贝浅拷贝真的是个有意思的东西,我之前真的不知道,数组和对象怎么还会有深浅拷贝这一概念,不就是通过 = 去赋值吗? 后面才知道,原来深浅拷贝就是依据是否改变原来对象或者数组的数值来区分的,这只是小白我认为,如有错误,请在评论中指出,大家一起学习。
下面给几段代码,注意观察输出情况
/*
* 例 1: 数组
*/
var a = [1, 2, 34, 65];
var b = a;
b[0] = 20;
console.log(a, b);
// 输出: (4) [20, 2, 34, 65] (4) [20, 2, 34, 65]
/*
* 例 2: 对象
*/
var obj_array = [1, 23, 4]
var obj1 = {
a: 20,
b: "zhouxiangpeng",
c: obj_array
}
var obj2 = obj1;
obj2.a = 40;
obj2.c = "修改了"
console.log(obj1, obj2);
// 输出: {a: 40, b: "zhouxiangpeng"} {a: 40, b: "zhouxiangpeng"}
从输出中可以看出:当通过 = 去赋值去创建新的数组或者对象时,如果改变新的数组或者对象的某个值,原数组也是会进行相应的改变!,因为这种是引用的原来的地址。
图解
重要注意的地方再强调一遍:所以从上面可以看出,如果是 “ = ” 赋值操作,那么新创建的与原来的都是去引用原来的地址,那么该地址的信息被修改会被同步到所有的被引用的主体中。这不是浅拷贝。
下面图解下浅拷贝原理
从图中可以看出来,浅拷贝其实就是拷贝数组或者对象的第一层数据。代码实现如下:
// 第一种方法实现浅拷贝
var newobj3 = {
};
var obj3 = {
a: 20
b: "zhouxiangpeng",
c: [2, 3, 4]
};
for (var item in obj3) {
// 判断是否是对象的私有属性
if (!obj3.hasOwnProperty(item)) {
break
}
newobj3[item] = obj3[item]
}
newobj3.a = "修改了"
newobj3.c[0] = "修改了"
console.log(obj3, newobj3)
// 第二种方法 es5 提供的 ... 扩展符
var obj3 = {
a: 20
b: "zhouxiangpeng",
c: [2, 3, 4]
};
let newobj3 = {
...obj3}
从运行结果可以看出:新对象 newobj3 对第一层进行修改不会污染原对象,但是对第二层或者以下都会改变原对象,这就是浅拷贝(浅克隆)。
第三种方式实现浅拷贝
// 第三种方法实现浅拷贝
var obj4 = {
a: 20,
b: {
name: "zxp"
},
c: "zxp"
};
var newobj4 = {
}
/*
* Object.assign(obj,source1,source2)方法是将source1,source2 的对象可枚举属性
* 都添加到obj里面,然后返回一个obj
*/
newobj4 = Object.assign(newobj4, obj4)
newobj4.c = "c修改了"
newobj4.b.name = "name修改了";
console.log(obj4, newobj4)
深拷贝 (深克隆) 就是对原对象的多层(所有层)进行克隆 (拷贝),克隆出的对象或者数组的属性或者属性值的改变不会影响原来的数组或者对象。所以这里就需要用到递归,这是最基本的想法,递归去遍历对象的属性判断是否是对象,从而进行操作。
递归实现深克隆(深拷贝)
function deepClone(obj) {
/*
* undefined null
*/
if (typeof obj !== Object) {
return obj
}
if (obj === null) {
return null
}
if (obj === undefined) {
return undefined
}
if (obj instanceof RegExp) {
return new RegExp(obj)
}
if (obj instanceof Date) {
return new Date(obj)
}
// 让新创建的对象指向原来对象的构造函数,那么他们就同属于一个类。
let newobj = new obj.construtor;
for (var key in obj) {
if (obj.hasOwnProperty(key)) {
newobj[key] = deepCopy(obj[key])
}
}
return newobj
}
总的上面的就是递归调用,不断的判断obj 的类型,从而实现添加到新对象中。还有一种方法实现深克隆,那就是通过 JSON.stringify 和 JSON.parse 这两个方法去实现深克隆,但是存在一些小问题,需要特别注意。
var obj = {
a:20,
b:"Hell Word!",
c:{
name:"zxp"
},
d:function(){
},
e:/[0,2]{x,y}\g/,
f:new Date()
};
var newobj = JSON.stringify(obj);
newobj = JSON.parse(newobj);
console.log(newobj)
观察上面代码输出会发现: 当对象中是 时间,函数,或者正则表达式 时会在 toString 方法调用输出时存在一些问题。 这是值得注意的地方。