- 在JS中,数据分为两大类:基本数据类型 和 引用数据类型;基本数据类型包括: string、number、Boolean、null、undefined、symbol、biglnt七大类,引用数据类型包括: Array、Object、Function。这两种数据类型的数据传递也有两种方式: 值复制 和 引用复制。
- 在内存中分为:栈(stack) 和 堆(heap)。简单数据类型存放在栈区,复杂(引用)数据类型存放在堆区。在开发时,可以直接操作栈区的变量,不能直接操作堆区的数据,堆区需要JS引擎来操作。
值复制
- 变量赋值
var a = 5,b = a
console.log('a:', a) // 5
console.log('b:', b) // 5
a = 9
console.log('a:', a) // 9
console.log('b:', b) // 5
由上可知: a = 5 是一个number类型, b 被赋值为 a, 但变量b被赋值时,赋的是a的值,而不是变量a本身。因此当变量a的值改变时,变量b的值没有影响。不仅是number类型,所有基本数据类型都是值赋值;
由图可知: 当设置一个变量a时,是在栈内存中开辟出一块内存,用于存放变量a以及它的值,当变量a的值改变时,栈区中对应内存的数据值也会发生改变;声明一个变量b,并把a赋值给变量b, 就是在栈内存中重新开发出一块新的内存空间,用于存放b,b的值是变量a传递的;
- 函数传参
let a = 3toProjectFunction(x) {const x = x + 2
}this.toProjectFunction(a) // 5 (x = 5)
console.log(a) // 3
函数传参:传递的是参数的值,而非参数本身,无论函数中的 x 如何变化,对外部的变量a没有任何影响;
综上:无论是变量赋值还是函数传参,只要是基本数据类型,都只是值传递,拷贝一份值的副本进行运用;
引用复制
注意: 复杂数据类型(Array、Object)里面如果也存在Array或Object, 也将存在引用赋值的情况;
- 当声明一个对象obj1(复杂数据类型)时,会在堆内存中开辟出一块新的内存空间,用于存放obj1的数据: 即 address1: { name: ‘zhao’, id: 2 };
- 但是变量obj1是存放在栈内存中的,栈内存中不能直接存放复杂数据类型的值,因此会存给obj1一个地址,用于指向堆内存中对应的值;
- 声明一个对象obj2(复杂数据类型),将obj1赋值给obj2,就是把obj1的地址赋给了obj2,此时obj1和obj2公用一个地址,也就是在堆中共享同一块内存空间,任何一个对象更改数据另外一个对象也会随之改变;
- 若重新给obj1赋值,相当于给obj1在堆中重新开辟一块新的内存空间,此时obj1 和 obj2 的地址不一样,即不再和obj2共享同一块内存空间,其中一个对象改变时,另外一个无影响;
注意:
- 若每次给obj2赋值时都想要互不影响,就要进行转换赋值JSON.parse(JSON.stringify()), 但是转换赋值后的对象如果作为函数参数传递时,仍然存在引用赋值情况;
- 引用赋值后如果将对象置空,相互之间没有影响;