以下所有变量都只针对复杂数据类型,在此不讨论基本数据类型。拷贝基本数据类型,原变量改变了,是不会影响赋值后的新变量的,因为基本数据类型不牵扯到堆栈之间的引用地址问题!!!
一、等号(=)赋值
先说最简单的等号赋值,其实就是“你变我也变”,等号(=)赋值就是会将原变量的引用地址和值一起赋值给新变量,即堆里面的值会有两个引用指针同时指向它。若 a = b,那么 如果a 里面的值变了,b 里面的值也会随之变化,看代码:
let a = [1, 2, 3, {b: 4, c: 5}
let b = a
console.log(a) // 打印结果是:[1, 2, 3, {b: 4, c: 5}
console.log(b) // 打印结果是:[1, 2, 3, {b: 4, c: 5}
a[0] = 111
a[3].b = 444
console.log(a) // 打印结果是:[111, 2, 3, {b: 444, c: 5}
console.log(b) // 打印结果是:[111, 2, 3, {b: 444, c: 5}
二、浅拷贝
浅拷贝有以下几种方式:
- Object.assign()
- Array.prototype.concat()
- Array.prototype.slice()
- 扩展运算符(…)
先说一下浅拷贝是什么含义
浅拷贝:即只拷贝浅浅一层值。只是增加了一个指针指向已存在的堆内存。
在这之前,我一直理解的浅拷贝和等号赋值一样,也是拷贝了引用地址和值,也会“你变我也变”,结果!并不是!今天仔细查看了官方文档,才发现是以下这种情况时才会“你变我也变”:
我就直接拿Object.assign()来举例吧,因为其他浅拷贝的方法道理是一样的
let obj1 = {
a: 1,
b: 2,
c: {d: 3, e: 4}
}
let obj2 = Object.assign({}, obj1)
console.log(obj1) // {a: 1, b: 2, c:{d: 3, e: 4}}
console.log(obj2) // {a: 1, b: 2, c:{d: 3, e: 4}}
// 1. 改变原对象的属性,新对象的属性并不会随之改变,即使某一属性是对象
obj1.a = 111
obj1.b = 222
obj1.c = {d: 333, e: 444}
console.log(obj1) // {a: 111, b: 222, c:{d: 333, e: 444}}
console.log(obj2) // {a: 1, b: 2, c:{d: 3, e: 4}}
// 2. 若原对象的某一属性值是一对象,改变这个属性对象中的属性,那新对象的该属性对象的属性也会随之改变
obj1.c.d = 333
obj1.c.e = 444
console.log(obj1) // {a: 1, b: 2, c:{d: 333, e: 444}}
console.log(obj2) // {a: 1, b: 2, c:{d: 333, e: 444}}
// 由这种情况可以看出obj1.c和obj2.c这两个对象的引用地址是一致的,地址指针都是指向{d: 3, e: 4},所以这两个对象是相关联的,你变我也变
// 3. 结合上面两种情况,再看这种情况,目前已知更改原对象的属性,新对象是不会随之改变的
obj1.c = {d: 333, e: 444}
console.log(obj1) // {a: 1, b: 2, c:{d: 333, e: 444}}
console.log(obj2) // {a: 1, b: 2, c:{d: 3, e: 4}}
// 那现在若再继续更改原对象的属性对象中的属性呢,新对象的c中的属性会不会随之改变呢
obj1.c.d = 888
obj1.c.e = 999
console.log(obj1) // {a: 1, b: 2, c:{d: 888, e: 999}}
console.log(obj2) // {a: 1, b: 2, c:{d: 3, e: 4}}
// 答案是不会随之更改
// 因为obj1.c更改之后,obj1.c这个对象的引用地址也随之改变了,便与obj2.c这个对象的引用地址不一样了
// 此时obj1.c和obj2.c的地址指针是指向各自的对象值了,互不联系,互不干扰了,你变我不变
// 所以此时更改obj1.c对象里面的属性,obj2.c对象的属性不会随之改变了
由上述代码可得出:
-
当原对象中的属性是对象时
- 改变原对象的这一属性,新对象不会变。(你变我不变)
- 改变原对象的这一属性对象中的属性,新对象会随之改变。(你变我也变)
- 若已改变了原对象的这一属性,再次更改这一属性对象中的属性时,新对象不会随之改变。(你变我不变)
-
当原对象中的属性不是对象时
- 改变原对象的这一属性,新对象不会随之改变。(你变我不变)
三、深拷贝
深拷贝有以下几种方式:
- JSON.stringify() (有缺陷,不全能)
- 递归函数 (较为常见,常用)
- lodash第三方插件 deepClone()
深拷贝:是栈里面新开辟了一个新空间,存放引用地址,并且在堆里面申请了一个新的内存空间存放值,再增加一个新指针指向这个新的内存。进行深拷贝后的这两个对象属性值完全相同,但是对应了两个不同的地址。所以,修改其中一个对象的属性,并不会改变另一个对象的属性。