也许我们都了解javascript中的基础类型有哪些:原始数据类型(Undefined、Null、Boolean、Number、String、Symbol),引用类型:Object
先看下面的场景,通过=赋值只是浅拷贝,因为属性是引用类型,拷贝的是内存地址,导致 其中一个对象(oldObj)改变了这个地址,就会影响到另一个对象(newObj)。这种情况怎么解决呢,当然是深拷贝了。可以通过下面的图了解下深浅拷贝的区别。
console.log(oldObj == newObj);//true,说明指向了同一个地址
const oldObj = {
a: 1,
b: {
c: '12',
d: '34',
},
d: function () {
console.log('555');
},
};
var newObj = oldObj;
newObj.a = '666';
newObj.b = {
c: 666,
d: 666,
};
console.log(oldObj);
//结果:
//a: "666"
//b: {c: 666, d: 666}
//d: ƒ ()
深拷贝就是从堆内存中开辟一个新的区域存放新对象,对对象中的子对象进行递归拷贝,拷贝前后的两个对象互不影响
深拷贝的实现方式:
1,JSON.parse(JSON.stringify())
parse方法可以将JSON字符串反序列化成JS对象,stringify方法可以将JS对象序列化成JSON字符串,这两个方法结合起来就能产生一个便捷的深克隆。
这个方法虽然可以解决绝大部分是使用场景,但是却有很多局限
1.1 他无法实现对函数 、RegExp等特殊对象的克隆
1.2 会抛弃对象的constructor,所有的构造函数会指向Object
1.3 对象有循环引用,会报错
下面先试下该方法的优越性,查看结果,深拷贝生效,改变newObj,oldObj不跟着改变。
打印下面,输出false,可进一步证明新旧对象
console.log(oldObj == newObj);//false
针对不能处理函数和正则,看下面对函数处理例子
<script>
const oldObj = {
a: 1,
b: {
c: '12',
d: '34',
},
d: person,
};
// var newObj = JSON.parse(JSON.stringify(oldObj));
var newObj = oldObj;
newObj.a = '666';
newObj.b = {
c: 666,
d: 666,
};
function person() {
var name = 'lj';
return name;
}
console.log(newObj.d);
</script>
打印结果为:
ƒ person() {
var name = 'lj';
return name;
}
但是换成json深拷贝,结果是
看正则处理的例子,新增一个属性e,再使用JSON.parse(JSON.stringify(),很可惜结果是
e: new RegExp('ab+c', 'i'),
console.log(oldObj.e); /ab+c/i
console.log(newObj.e); /{}
//空的,得到的正则就不再是正则(变为空对象)
2、函数库lodash的_.cloneDeep方法
该函数库也有提供_.cloneDeep用来做 Deep Copy,转自思否讲的很详细
var _ = require('lodash');
var obj1 = {
a: 1,
b: { f: { g: 1 } },
c: [1, 2, 3]
};
var obj2 = _.cloneDeep(obj1);
console.log(obj1.b.f === obj2.b.f);// false
3、手写深拷贝
这个单独章节写个文章分享吧
4、项目遇到的问题
如下,调用接口获取到后端数据,并赋值给 that.cityList
that.cityList = res.data;
that.cityList在data数据结构如下
cityList: {
priceA: "",
driverP: "",
driverP "",
dynamicPList: [{beginTime:8:00,endTime:10:00},{},{}]
},
我后面是需要将该对象里的数组遍历渲染出表单的形式,因涉及公司,这里不贴图片,总之就是说我需要展示并可修改cityList.dynamicPList每一项的值(即双向绑定),使用的element里的组件timepicker展示,该组件要求绑定一个数组,表示起始和结束时间,即我要在赋值的cityList中新创建一个变量[ item.time = [item.beginTime, item.endTime];],这时候就遇到问题了,时间修改不了,但是怎么解决呢?这就用到深拷贝了。
因为我新创建的属性每个item.time的值需要在页面双向绑定,但因为他指向了res.data,而res.data的数据是后端的数据,res.data是数组,属于引用类型,你改变cityList.dynamicPList中的item.time时,他的值总是指向res.data,但是这个数据vue根本没有检测到,因此数据无法实时改变,页面上表现为时间无法更改,除非你通过
this.cityList = JSON.parse(JSON.stringify(this.cityList));
解除数组的引用,其指针不再指向res.data,因此数据可以改变了。这是用到了深拷贝的地方。