一、前言
JS的数据类型分为:基本数据类型和复杂数据类型。这两种不同的数据类型的数据的存放方式是有区别的,所以在拷贝赋值的时候就产生了深拷贝和浅拷贝的问题。
二、数据类型和存放位置
2.1、基本数据类型及其存放位置
JS的基本数据类型一般有5种:number(数字)、string(字符串)、boolean(布尔类型)、null(null型)、undefined(underfind型)。
存放位置:存储在栈里面,取用赋值的时候是值传递。
2.2、复杂数据类型及其存放位置
JS的复杂数据类型指引用数据类型,就是Object。像array,Object两种类型就属于复杂数据类型。
存放位置:它的指针(可以想象成路牌)一般存在栈中,值(可以想象成目的地)存在堆中(我们通过路牌找到目的地,这里则是通过指针找到我们所需要的值)。
三、一窥“浅拷贝和深拷贝”真容
3.1、区分深拷贝与浅拷贝
通过表象区分深拷贝与浅拷贝,假设变量B复制了变量A,当修改变量A时,看变量B是否会发生变化,如果变量B也跟着变了,说明这是浅拷贝。如果变量B没变,那就是深拷贝。
代码说明:
浅拷贝(直接赋值):
简单数据类型:
var a = 7;
b = a; // 栈内存会开辟一个新的内存空间,此时b和a都是相互独立的
b = 2;
console.log('a=', a); // a=7
复杂数据类型:
let a=[0,1,2,3,4],
b=a;
console.log(a===b); // true
a[0]=1;
console.log(a,b); // [1,1,2,3,4] [1,1,2,3,4]
如果是基本数据类型,名字和值都会储存在栈内存中。但是复杂数据类型就会一改全改,因为它真正的数据是存在堆内存中的,并没有被复制成两个。
图解浅拷贝过程
复杂数据类型在栈和堆中存储状态:
复杂数据类型的浅拷贝结果:
复杂数据类型浅拷贝后的修改结果:
深拷贝:
由上图可知,在对复杂数据类型实现深拷贝的时候,不仅要拷贝栈内存中,还需要拷贝堆内存中的值。一般可以采用两种方式:
递归拷贝所有层级属性:
function deepClone(obj){
let objClone = Array.isArray(obj)?[]:{};
if(obj && typeof obj==="object"){
for(key in obj){
if(obj.hasOwnProperty(key)){
//判断ojb子元素是否为对象,如果是,递归复制
if(obj[key]&&typeof obj[key] ==="object"){
objClone[key] = deepClone(obj[key]);
}else{
//如果不是,简单复制
objClone[key] = obj[key];
}
}
}
}
return objClone;
}
let a=[1,2,3,4],
b=deepClone(a);
a[0]=2;
console.log(a,b);
通过JSON对象来实现深拷贝:
function deepClone2(obj) {
var _obj = JSON.stringify(obj),
objClone = JSON.parse(_obj);
return objClone;
}
**注意!**但是这种方法有一个缺点,无法实现对对象中方法的深拷贝,会显示为undefined。
图解深拷贝
3.2、一些说明
在实际应用中,浅拷贝和深拷贝是相对的,并不是绝对的。具体实现浅拷贝或者深拷贝要根据数据的结构采用具体的方法。举个栗子,for…in循环拷贝对象(数据),对于一层结构的对象一次循环拷贝就实现了深拷贝,但是如果这个对象数据是多层嵌套的数据格式,这次操作人就是浅拷贝。
参考链接:
js浅拷贝与深拷贝的区别和实现方式;
JS的数据深拷贝和浅拷贝。
欢迎大家一起讨论、学习。