ECMAScript变量包含两种不同数据类型的值:基本类型值和引用类型值。基本类型值是简单的数据段,而引用类型值指那些可能由多个值构成的对象。
在将一个值赋给变量时,解析器必须确定这个值是基本类型还是引用类型。基本类型包括如Undefined、Null、Boolean、Number和String,这5种基本类型数据类型是按值访问的,因此可以操作保存在变量中的实际的值;引用类型类型的值是保存在内存中的对象。与其他语言不同,JavaScript不允许直接访问内存中的位置,也就是说不能直接操作对象的内存空间。在操作对象时,实际上实在操作对象的引用而不是实际的对象,因此,引用类型的值是按引用访问的。
1、动态的属性
定义基本类型和定义引用类型的方法大相近庭。对于引用类型的值,我们可以为其添加属性和方法,也可以改变和删除其属性和方法,如下:
2、复制变量值
如果从一个变量向另一个变量复制基本类型的值,会在变量对象上创建一个新值,然后将该值复制到为新变量分配的位置上。
当从一个变量向另一个变量复制引用类型的值时,同样也会将存储在变量对象中的值复制一份放到为新变量分配的内存空间里。不同的是,这个值实际上是一个指针,而这个指针执行存储在堆中的一个对象。复制结束后,两个变量实际上将引用同一个对象。因此,改变其中一个变量,就会影响到另一个变量,如下所示:
3、传递参数
ESMAScript中所有函数的参数都是按值传递的。也就是说,把函数外部的值复制给函数内部的参数,就是把值从一个变量复制到另一个变量一样。基本类型值的传递如同基本类型变量的复制一样。引用类型值的传递,则如同引用类型变量的复制一样。有不少开发者在这一点上可能感到困惑,因为访问变量有按值和按引用两种方式,而参数只能按值传递。
在向参数传递基本类型的值时,被传递的值会被复制给一个局部变量(即命名参数)。如下代码所示:
在向参数传递引用类型的值时,会把这个值在内存中的地址复制一个给局部变量,因此这个局部变量的变化会反映在函数的外部。在这里我们使用引用类型来看一下:
在这个函数内部,obj和person引用的是同一个对象。换句话说,即使这个对象是按值传递的,obj也会按引用来访问同一个对象。于是,当在函数内部为obj添加name属性后,函数外部的person也会有所反映,因为person指向的对象在堆内存中只有一个,而且是全局对象。很多开发者错误的认为:在局部作用域中修改的对象会在全局作用域中反映出来,就说明参数是按引用传递的。为了证明对象是按值传递的,我们在看看下面这个进过修改的例子:
上面这个例子可以看出,如过person是按引用传递的,那么person就会自动被修改为指向其name属性值为"sdf"的新对象。但是,当接下来再访问person.name时,显示的仍然是"zxj"。这说明即使在函数内部修改了参数的值,但原始的引用仍然保存不变。实际上,当在函数内部重写obj时,这个变量引用的就是一个局部对象了。而这个局部对象会在函数执行完毕时立即销毁。
可以将ECMAScript函数的参数想象成局部变量。
4、检测类型
虽然在检测基本数据类型时typeof是一个得力助手,但是在检测引用类型时,这个操作符用处却不大。通常,我们并不想知道某个值是对象,而是想知道他是什么类型的对象。为此ECMAScript提供了instanceof操作符,其语法如下: