一、什么是原始值?什么是引用值?
- 原始值:存储在栈(stack)中的数据,他们的值是直接存储在变量的存储空间中。
- 引用值:存储在堆(heap)中的对象,存储在变量中的值是一个指针,它指向实际存储对象的内存地址。
- 原始类型有: Number、String、Boolean、Undefined、Null,Symbol(es6)类型
- 引用类型有: 数组(Array)、对象(Object)、函数(Function)、类(class)等等
二、怎么存储?
js引擎将内存划分为两种内存空间: 堆和栈,程序执行前,引擎会为数据分配内存空间,不同类型数据会分配到不同内存空间中。
- 栈存储
系统自动分配固定的内存,数据先进后出,像一个有底没顶的箱子,线性结构。由于原始类型数据的值是固定大小的,所以存放在栈中,方便查找。基本类型的值是按值访问的,因为可以操作保存在变量中的实际值。
2、堆存储
系统动态分配内存,像一个书架,怎么拿怎么放,散列结构。引用值是指由多个数据值组成的对象,值大小未知,因此存储在堆中,由于js不允许直接访问堆中的位置,因此栈中会存储该对象在堆中的引用地址(引用地址的大小是固定的),指向堆中的对象。在访问时,会通过栈中存储的引用地址找到堆中的对象。
三、值的赋值
- 原始值
1)自己重新赋值:
原始值有个特点叫不可改变的原始值,就是一个内存空间已经存储值了,就不能在这个内存空间上改变数据值,而是开辟另一块内存并命名,而原来的内存空间的命名被擦除掉而找不到这个内存空间。
2)赋值给别人:
赋值给别人时也一样,会在栈中产生一个新的副本,因此复制的值和原来的值之间没有任何联系,他们各自位于不同的栈区。
let number1 = 2;
let number2 = number1;//将number1的值复制给number2
2、引用值
当一个变量向另一个变量赋值引用值时,复制的是地址,因为不能直接操作堆中的数据,所以只能复制地址,因此arr1和arr都指向[1,2],如果arr1的内容改变,那么arr也会受到影响。如果arr重新赋值,更换引用地址,arr1不会受到影响。
四、值的传递
- 原始值
当把原始值传递给函数的参数时,参数是全新的副本,在函数中修改参数值,并不会影响原来的值。
let number = 1;
function num(n:number){
n = 8;
console.log(n);//输出8
}
num(number);
console.log(number);//输出1
- 引用值
1)当把引用值传递给函数时,传递给函数的是对原值的引用,在函数内部可以使用此引用来修改对象本身的值。
let object = {property:1};
function testObject(para:any){
para.property = 2;
}
testObject(object);
console.log(object);//输出{property:2};
2)如果给函数的参数赋予新值,引用就会发生改变,指向另外的堆地址,参数和原有的对象不再有任何关系,两者之间互不影响。
let object = {property:1};
function testObject(para:any){
para={property:2};
}
testObject(object);
console.log(object);//输出{property:1};
五、值的比较
- 原始值
比较的是值本身,而不是栈的位置。
let number1 = 7;
let number2 = 7;
console.log(number1==numner2)//true
- 引用值
比较的是引用地址,看他们引用的是否是同一个对象,而不是比较他们的字节信息是否相同。即使两个引用值的对象相同,但是地址不一样,也是不相同的。
let object1 = {property:1};
let object2 = {property:1};
console.log(object1==object2);//false
let object3 = object1;
console.log(object1==object3);//true
object1.property = 3;
console.log(object1==object3);//true