js复制拷贝数据时的注意事项
昨天教导新人的时候,发现她不了解复制数据时的区别,以为a=b之后赋值之后之后就没有关联了,因此今天写这篇文章自己整理一下这中间要注意的东西。这篇先写因为数据类型导致的复制区别,下篇写深拷贝浅拷贝。
1、复制数据时的划分
在代码中,经常会用到需要将a赋值给b的情况。但是a的值得类型往往会影响到拷贝之后a、b之间是否会有联系。
js的基础数据类型有以下几种:
Undefined、Null、布尔值(Boolean)、字符串(String)、数值(Number)、对象(Object),es6又新增了一种Symbol。
引用数据类型有
2、基础数据类型的复制
当a为基础数据类型时,b复制了a的值,然后再改变a的值,最后打印时,a的值改变了,但是b的值不会改变。这种实际上是在内存中给b另外创建了一个控件储存它的数据,,所以之后a不管再怎么变都不会影响到b了。
举例:就和我们平时复制文件夹一样,复制了一个一抹一样的文件,但是之后分别这两个文件的增删改查都不会影响到对方。
let a = 12;
let b = a;
a = 13;
console.log('a:' + a);
console.log('b:' + b);
3、引用数据类型的复制
当a为引用数据类型时,如Object类型,b复制了a的值之后,再改变a的值,之后打印a、b,可以发现a、b的值都改变了。这是因为引用数据类型在栈内存中保存的实际上是对象在堆内存中的引用地址。通过这个引用地址可以快速查找到保存中堆内存中的对象。然后在复制时给b的时一个引用地址,但是a与b的引用地址指向的是同一个内存,也就是a之前所指向的内存地址,所以只要改变a或者b,改变的都是他们的内存地址中的值,因此只要有改动,两个都会一起发生改变。
举个例子:
引用数据类型复制时的情况就像是一间房间,有好几个入口,储存数据所在的内存就是房间,本来只有a一个入口,后来又增加了b这个入口a与b就是这个房间的不同的入口,当从a或者b入口进去把房间里的家具改变之后,房间没有变,入口也没有变,但是再从a或者b进去之后看到的房间内部都已经变化了,但依然是同一个房间。
let a = {
id: '0000001',
name: '小明'
};
let b = a;
a.id = '000002';
a.id = '小王';
console.log('打印a的值');
console.log(a);
console.log('打印b的值');
console.log(b);
4、避免引用数据类型复制后的数据关联
很多时候复制后的数据并不想和原来的数据关联起来,这个时候就要对引用数据类型的复制进行处理了,原理就是把引用数据类型中的每一个基础类型一个个复制过去。这样就会另外分配一个内存地址,不会和之前的共用同一个了。
let a = {
id: '0000001',
name: '小明'
};
// 把a的值复制过去
let b = {
id: a.id,
name: a.name
};
a.id = '000002';
a.id = '小王';
console.log('打印a的值');
console.log(a);
console.log('打印b的值');
console.log(b);
结果如下,a改变后b没有改变,依然是之前复制好的值,
但是这种是最简单的情况,很多时候要复制的引用类型的属性值很多,甚至还可能嵌套。这种时候如果一个个手动拆开来复制的话太过麻烦,就需要优化写法了,比如一个递归遍历对象的方法,这里不再赘述,另起一篇详细描述。
let a = {
id: '0000001',
name: '小明',
data: {
remark: '这是个嵌套对象',
id: '0000001001'
}
};