深拷贝/浅拷贝
4-1
赋值
数据的复制有两种情况,一直是值复制,一种是引用赋值({},[],function)
var a = 2;
var b = a;
a = 4;
console.log(b)//2 值复制,a的值改变不影响b
//接下来看引用类型的复制
var arr1 = {a: 1};
var arr2 = arr1;
console.log(a === b); // 输出true
console.log(arr2.a);// 1
arr1.a = 2;
console.log(arr2.a);// 2
一般我们的利用“=”赋值只是单纯的引用赋值
浅拷贝
先说结论:浅拷贝只复制一层对象的属性,并不包括对象里面的为引用类型的数据
我按照两个方面来整理
-
1. 仅仅针对数组类型的浅拷贝方法
- slice()方法:他是将原数组中抽离部分出来形成一个新数组。我们只要设置为抽离全部,即可完成数组的浅拷贝
- concat()方法:这个代码也非常简单,原理更加粗暴。它是用于连接多个数组组成一个新的数组的方法。那么,我们只要连接它自己,即可完成数组的浅拷贝
-
2. 可以对数组以及对象的浅拷贝方法
- hasOwnProperty方法
- es6的扩展运算符
仅仅针对数组类型的浅拷贝方法
var arr0 = [1,2,[3,4]];
var arr3 = arr0.slice();//slice方法
//concat方法
// var arr3 = arr0.concat();
//es6的扩展运算符
var [ ...arr3 ] = arr;
console.log(a === b); // 输出false,说明外层数组拷贝的是实例,不是引用
arr3[0] = 100;
console.log(arr0[0]);// 1
console.log(arr3[0]);// 100
// 如果我们修改arr0的第二层呢,也就是说属性1,2,[3,4]是第一层数据,而[3,4]还是引用类型的,就是第二层数据
arr3[2][0] = 99;
console.log(arr0[2][0]);//99 发现obj0也改变了
console.log(arr3[2][0]);//99
可以对数组以及对象的浅拷贝方法
我们可以考虑用对象的hasOwnProperty方法来进行浅拷贝,可以对数组[],对象{}进行浅拷贝
var obj2 = {
a: 12,
b: function() {
console.log('1');
},
'c' : [1,[2,3],[4,5]],
};
//hasOwnProperty方法
var obj3 = shallowCopy(obj2);
function shallowCopy(src) {
var dst = {};
for (var prop in src) {
if (src.hasOwnProperty(prop)) {
dst[prop] = src[prop];
}
}
return dst;
}
// ES6的扩展运算方法
var {...obj3} = obj2;//如果obj2是对象
var [...obj3] = obj2;//如果obj2是数组
//step1: 改变属性a,属性a是单纯的基础类型
obj3.a = "mike3";
console.log(obj1.a);//mike2
console.log(obj3.a);//mike3 可以发现其实obj1的a是实例拷贝,而不是引用拷贝
// step2: 改变属性b(属性b是函数)
obj3.b = function() {
console.log('改变后的');
};
console.log(obj3.b);
// ƒ () {
// console.log('改变后的');
// }
console.log(obj2.b); // 改变obj3的属性b,但是obj2的b不变
// ƒ () {
// console.log('1');
// }
// step3: 改变属性c,属性c是数组,一个引用类型
// 如果我们修改obj1的第二层呢,也就是说属性a,b,c是第一层数据,而c的值还是引用类型的话,那c的属性值就是第二层数据
obj3.c[0] = 99;
console.log(obj1.c);//[99, [2,3],[4,5]] obj1.c也改变了
console.log(obj3.c);//[99, [2,3],[4,5]]
//可以发现:第二层的数据的拷贝依旧是引用拷贝!!
- 这种情况,为什么obj3的a改变,obj2的a没有改变,其实是拷贝成功了,把引用类型进行实例的拷贝(也就是值复制);
- obj2的属性b是一个函数,但是该方法也可以对其进行值拷贝;我没太弄明白?
- 但是obj2的属性c又是一个引用类型。可以发现,浅拷贝仅仅是对外层的进行实例拷贝,如果其属性元素的值(那就是内层的子数组)为引用类型的值时,则内层元素仍然是拷贝引用。
常用方法为:Array.prototype.slice(), Array.prototype.concat(), jQury的$.extend({},obj),例:
var a = [{c:1}, {d:2}];
var b = a.slice();
a[0].c = 3;
console.log(b[0].c); // 输出 3,说明其元素拷贝的是引用
4-2
深拷贝
深拷贝后,两个对象,包括其内部的元素互不干扰。常见方法有JSON.parse(),JSON.stringify(),jQury的$.extend(true,{},obj),lodash的_.cloneDeep和_.clone(value, true);但是各有不同
-
1. 仅对对象{}进行浅拷贝方法
- 转换成json再转换成对象实现对象的深拷贝
var obj = {
name: 'man',
other: {
a: 1
}
}
var obj2 = JSON.parse(JSON.stringify(obj))
console.log(obj2.other)// {a: 1}
obj2.other.a = 2;
console.log(obj.other);// {a: 1} 可以看出,修改obj2的第二层的数据,obj依旧不变,所以是深拷贝
console.log(obj2.other);//{a: 2}
- 还有其他的深拷贝方法
Underscore —— _.clone()/jQuery ——
.extend()/lodash —— _.clone() / _.cloneDeep() 这里不细说了,毕竟这些个封装好的,我们可以调用该函数