【小滴课堂】神奇的JS深浅拷贝

61 阅读3分钟

有很多小伙伴会对深浅拷贝有疑惑,到底什么是深拷贝什么是浅拷贝呢?

在这个之前,我们要知道堆与栈分别是什么。

栈:String、Number、Boolean、Null、Underined

堆:Function、Array、Object

然后我们根据上面的理解来认知接下来的深拷贝与浅拷贝

什么是浅拷贝

两个引用类型指向同一个地址,改变一个,另一个也会随之改变

我们来看一个demo

var c = { num: 18 };
var d = c;
c.num = 20;
console.log('c:', c, 'd:', d);

我们看一下浏览器

上面其实理解非常简单,我声明了一个对象c,因为对象复杂数据类型,存放于堆内存的,所以在栈中留下了一个指向地址指向堆中的num=18

然后我在栈中声明了一个变量d,并将c赋值给d,c是指向堆中的num,所以,d拷贝了c的指向路径当c指向的num改变了,d也会随着改变,这就是浅拷贝

我们可以看一下这个图理解一下

什么是深拷贝

复制后引用类型指向一个新的内存地址,两个对象改变互不影响

我们看一下demo

var c = { num: 18 };
var d = JSON.parse(JSON.stringify(c))
c.num = 20;
console.log('c:', c, 'd:', d);

再去观察一下浏览器

这里我们也可以这样去理解,我们在栈中声明了一个对象c并指向与堆中的num=18。

然后再在栈中声明一个变量d,然后使用序列化与反序列化的操作,将对象c深拷贝给d

然后再去修改原来c所指向的堆中的num。这两个对象是互不干扰的,因为是深拷贝的缘故,所以这样我们就实现了对象的深拷贝与浅拷贝

然后我们再思考一下,如果是基础数据类型的赋值他是属于深拷贝还是浅拷贝呢?

答案是:赋值既不是深拷贝也不是浅拷贝,只是跟深拷贝是类似

我们简单在浏览器看一下就知道,因为这是简单数据类型,不用指向堆的地址,所以可以直接的进行赋值

所以它并不是深拷贝与浅拷贝

我们再看一下,数组的方法:concat、slice是浅拷贝还是深拷贝?

concat() 方法用于连接两个或多个数组。该方法不会改变现有的数组,而仅仅会返回被连接数组的一个副本

我们写一个demo

var arr1 = [1,2,3];
var arr2 = arr1.concat();
arr2[1] = 10;
console.log("数组的原始值:" + arr1 );
console.log("数组的新值:" + arr2 );

看看浏览器打印

通过JS的concat方法,改变拷贝出来的数组的某项值后,对原来数组没有任何影响

对于array对象的slice函数,返回一个数组的一段。(仍为数组)

我么看看demo

var arr1 = [1,2,3];
var arr2 = arr1.slice(0);
arr2[1] = 20;
console.log("数组的原始值:" + arr1 );
console.log("数组的新值:" + arr2 );

看看浏览器打印

通过JS的slice方法,改变拷贝出来的数组的某项值后,对原来数组没有任何影响

由于数组内部属性值为引用对象,因此使用slice和concat对对象数组的拷贝,整个拷贝还是浅拷贝,拷贝之后数组各个值的指针还是指向相同的存储地址