原始值 引用值
ECMAScript变量可以包含两种不同类型的数据:原始值和引用值。
原始值( primitive value)就是最简单的数据,引用值( reference value)则是由多个值构成的对象。
访问方式
在把一个值赋给变量时,JavaScript引擎必须确定这个值是原始值还是引用值。
- 6种原始值:Undefined、Null、Boolean、Number、string和symbol。保存原始值的变量是按值(byvalue )访问的,因为我们操作的就是存储在变量中的实际值。
- 引用值是保存在内存中的对象。与其他语言不同,JavaScript 不允许直接访问内存位置,因此也就不能直接操作对象所在的内存空间。在操作对象时,实际上操作的是对该对象的引用( reference)而非实际的对象本身。为此,保存引用值的变量是按引用( by reference)访问的。
动态属性
对于引用值而言,可以随时添加、修改、删除其属性和方法。
原始值不能有属性,尽管在尝试给原始值添加属性不会报错。
只有引用值可以动态添加后面可以使用的属性。
注意,原始类型的初始化可以只使用原始字面量形式。
如果使用的是new关键字,则JavaScript会创建一个object类型的实例,但其行为类似原始值。
下面来看看这两种初始化方式的差异:
let name1 = "Nicholas" ;let name2 = new string ( "Matt " ) ;name1.age = 27 ;name2.age = 26 ;console.log (name1.age ) ;// undefinedconsole.log ( name2.age) ;//26console.log (typeof name1) ; // stringconsole.log ( typeof name2) ; //object
复制值
除了存储方式不同,原始值和引用值在通过变量复制时也有所不同。
在通过变量把一个原始值赋值到另一个变量时,原始值会被复制到新变量的位置。请看下面的例子:
let numl = 5;let num2 = numl;
这里,num1包含数值5。当把num2初始化为num1时,num2也会得到数值5。这个值跟存储在num1中的5是完全独立的,因为它是那个值的副本。
这两个变量可以独立使用,互不干扰。这个过程如图4-1所示。
在把引用值从一个变量赋给另一个变量时,存储在变量中的值也会被复制到新变量所在的位置。
区别在于,这里复制的值实际上是一个指针,它指向存储在堆内存中的对象。
操作完成后,两个变量实际上指向同一个对象,因此一个对象上面的变化会在另一个对象上反映出来,如下面的例子所示:
传递参数
ECMAScript中所有函数的参数都是按值传递的。这意味着函数外的值会被复制到函数内部的参数中,就像从一个变量复制到另一个变量一样。
如果是原始值,那么就跟原始值变量的复制一样,如果是引用值,那么就跟引用值变量的复制一样。
变量有按值和按引用访问,而传参则只有按值传递。
在按值传递参数时,值会被复制到一个局部变量(即一个命名参数,或者用ECMAScript的话说,就是arguments对象中的一个槽位)。在按引用传递参数时,值在内存中的位置会被保存在一个局部变量,这意味着对本地变量的修改会反映到函数外部。(这在ECMASeript中是不可能的。)来看下面这个例子:
:::warning 原始值与引用值的区别:
- 原始值存储在栈中,引用值存储在堆中
- 原始值是以值的拷贝方式进行赋值,值是不可变的;
- 引用值是以引用的拷贝方式进行赋值,值是可变的
- 原始值的比较是值的比较,引用值的比较是引用的比较(比较引用的是否为同一对象) :::
