在JS里面,俗话说“万物皆对象”,那么对于引用型对象的复制,就存在浅复制和深复制的问题。
(注意:js基本数据类型变量不存在浅复制和深复制问题)
1、浅复制
对象引用变量的的复制,复制操作之后的两个变量会相互影响,一个的变量的值改变了,另一个变量的值也会随之改变。
2、深复制
产生一个对象的副本,完全独立于原来的对
象,副本的属性发生改变并不会影响原来的对象的属性。
3、出现浅复制和深复制的深层原因
计算机利用堆栈存储数据信息,栈内存比较小,但在栈内存中访问数据速度很快,堆内存容量比较大,但访问速度相对较慢。
JS基本数据类型数据简单,直接存储在栈内存里,而引用型对象
会在栈内存中存引用指针变量(指向对象实体在堆内存的地址)。
3.1 JS基本数据类型:
let a=123;
let b=a;
b=456;
console.log(b);
//456
console.log(a);
//123
变量名 | 值 |
---|---|
a | 123 |
b | 123 |
直接存储在栈中,值的改变不会相互的影响。
3.2 JS引用数据类型
class Person{
constructor(name,age){
this.name=name;
this.age=age;
}
}
let p1=new Person("zhangsan",123);
let p2=p1;
p2.name="lisi";
console.log(p1.name);
//lisi
在计算机堆栈中p1和p2的情况如下:
变量 | 值 |
p1 | 堆地址1 |
p2 | 堆地址1 |
地址 | 对象实体 |
堆地址1 | { name:"zhangsan",age:123 } |
每次取对象的属性时,先判断是否是简单数据类型,若为简单数据类型,计算机在栈中直接找到变量对应的值;若为引用数据类型,计算机先在栈内存中找变量对应的值(即堆地址编号),然后再根据该值(堆地址编号)去堆找到对应的实体,接着进行后续的操作。
4、浅复制和深复制的应用场景
4.1 基本数据类型复制:直接赋值,不用考虑浅复制和深复制。
4.2 引用数据类型:
4.2.1浅复制:直接通过赋值进行复制,传递对象的引用。
例如: 数组的复制:
let a=['mary','kangkang','michael'];
let b=a;
console.log(b);
//['mary','kangkang','michael']
注意:数组的slice()方法和concat()方法等,并没有进行深复制,只是复制了对象的一层属性。
let a=[1,2,3];
let b=[a,4,5,6];
let c=b.slice(0);
a.push(100);
console.log(a);
//[1,2,3,100]
console.log(c);
//[[1,2,3,100],4,5,6],a的值发生改变,c的值也会发生改变,所以c并没有对b进行深层复制。
4.2.2 深复制:很多时候需要堆对象进行复制,将对象的所有属性重新拷贝一份,之后完全独立于原对象。
function getType(obj){
//tostring会返回对应不同的标签的构造函数
let toString = Object.prototype.toString;
let map = {
'[object Boolean]' : 'boolean',
'[object Number]' : 'number',
'[object String]' : 'string',
'[object Function]' : 'function',
'[object Array]' : 'array',
'[object Date]' : 'date',
'[object RegExp]' : 'regExp',
'[object Undefined]': 'undefined',
'[object Null]' : 'null',
'[object Object]' : 'object'
};
if(obj instanceof Element) {
return 'element';
}
return map[toString.call(obj)];
}
function deepClone(data){
let type = getType(data);
let obj;
if(type === 'array'){
obj = [];
} else if(type === 'object'){
obj = {};
} else {
//不再具有下一层次
return data;
}
if(type === 'array'){
for(let i = 0, len = data.length; i < len; i++){
obj.push(deepClone(data[i]));
}
} else if(type === 'object'){
for(let key in data){
obj[key] = deepClone(data[key]);
}
}
return obj;
}