1.动态属性
原始值和引用值的定义方式很类似,都是创建一个变量,然后给它赋一个值。不过,在变量保存了 这个值之后,可以对这个值做什么,则大有不同。对于引用值而言,可以随时添加、修改和删除其属性和方法。比如,看下面的例子:

原始值不能有属性,尽管尝试给原始值添加属性不会报错。比如
2.复制值
原始值和引用值在通过变量复制时也有所不同。在通过变量把一个原始值赋值
到另一个变量时,原始值会被复制到新变量的位置。请看下面的例子:

这里,
num1
包含数值
5
。当把
num2
初始化为
num1
时,
num2
也会得到数值
5
。这个值跟存储在 num1 中的
5
是完全独立的,因为它是那个值的副本。
这两个变量可以独立使用,互不干扰。
在把引用值从一个变量赋给另一个变量时,存储在变量中的值也会被复制到新变量所在的位置。区
别在于,这里复制的值实际上是一个指针,它指向存储在堆内存中的对象。

在这个例子中,变量
obj1
保存了一个新对象的实例。然后,这个值被复制到
obj2
,此时两个变
量都指向了同一个对象。在给
obj1
创建属性
name
并赋值后,通过
obj2
也可以访问这个属性,因为 它们都指向同一个对象。图 4-2
展示了变量与堆内存中对象之间的关系。

3.传递参数
ECMAScript
中所有函数的参数都是按值传递的。这意味着函数外的值会被复制到函数内部的参数
中,就像从一个变量复制到另一个变量一样。如果是原始值,那么就跟原始值变量的复制一样,如果是 引用值,那么就跟引用值变量的复制一样。
,如果是 引用值,那么就跟引用值变量的复制一样。对很多开发者来说,这一块可能会不好理解,毕竟变量有按 值和按引用访问,而传参则只有按值传递。
俩面两个例子:

这一次,我们创建了一个对象并把它保存在变量
person
中。然后,这个对象被传给
setName()
方法,并被复制到参数
obj
中。在函数内部,
obj
和
person
都指向同一个对象。结果就是,即使对象 是按值传进函数的,obj
也会通过引用访问对象。当函数内部给
obj
设置了
name
属性时,函数外部的 对象也会反映这个变化,因为 obj
指向的对象保存在全局作用域的堆内存上。很多开发者错误地认为, 当在局部作用域中修改对象而变化反映到全局时,就意味着参数是按引用传递的。为证明对象是按值传 递的,我们再来看看下面这个修改后的例子:

这个例子前后唯一的变化就是
setName()
中多了两行代码,将
obj
重新定义为一个有着不同
name
的新对象。当
person
传入
setName()
时,其
name
属性被设置为
"Nicholas"
。然后变量
obj
被设置
为一个新对象且
name
属性被设置为
"Greg"
。如果
person
是按引用传递的,那么
person
应该自动将 指针改为指向 name
为
"Greg"
的对象。可是,当我们再次访问
person.name
时,它的值是
"Nicholas"
, 这表明函数中参数的值改变之后,原始的引用仍然没变。当 obj
在函数内部被重写时,它变成了一个指 向本地对象的指针。而那个本地对象在函数执行结束时就被销毁了。
4.确定类型
typeof
操作符最适合用来判断一个变量是否为原始类型。更确切地说,它是判断一
个变量是否为字符串、数值、布尔值或
undefined
的最好方式。如果值是对象或
null,返回
"object"
,
typeof
虽然对原始值很有用,但它对引用值的用处不大。
如果变量是给定引用类型(由其原型链决定,
)的实例,则
instanceof
操作
符返回
true;
即:一个构造函数的prototype属性是否出现在某个实例对象的原型链上。
8