我们还可以从用法的角度对其进行概括。任何语言、任何数据类型,它的数据都可以分为两大类:值类型和引用类型。
值类型:也称为原始数据或原始值(primitive value)。这类值存储在栈(stack)中,栈是内存中一种特殊的数据结构,也称为线性表,栈按照后进先出的原则存储数据,先进入的数据被压入栈底,最后插入(push)的数据放在栈顶,需要读取数据时从栈顶开始弹出(pop)数据,即最后一个数据被第一个读出来。因此说,值类型都是简单的数据段。变量的位置和变量值的位置是重叠的,也就是说值类型的数据被存储在变量被访问的位置。
引用类型:这类值存储在堆(heap)中,堆是内存中的动态区域,相当于自留空间,在程序运行期间会动态分配给代码和堆栈。堆中存储的一般都是对象,然后通过一个编号传递给栈内变量,这个编号就是所谓的引用指针(point),这样变量和变量值之间是分离的,它们通过指针相联系。当读写数据时,计算机通过变量的指针找到堆中的数据块,并进行操作。
在JavaScript中,number、string、boolean和undefined型数据都是值类型。由于值类型数据占据的空间都是固定的,所以可以把它们存储在狭窄的内存栈区。这种存储方式更方便计算机进行查找和操作,所以执行速度会非常快。
而对于object型数据(包括function和array)来说,由于它们的大小是不固定的,只能被分配到堆区(如果存储在栈区,则会降低计算机寻址的速度)。然后在栈区存储对象在堆区的地址即可,对于变量的性能也没有任何负面影响。
在JavaScript语言中,object、function和array等对象都是引用型数据。很多语言都把字符串视为引用型数据,而不是值类型,因为字符串的长度是可变的。但是JavaScript比较特殊,它把字符串作为值类型进行处理。不过,字符串在复制和传递运算中,是以引用型数据的方法来处理的。
当比较两个引用值时,比较的是两个引用地址,看它们引用的原值是否为同一个副本,而不是比较它们的原值字节是否相等
var a = new Number(1); // 引用值a
var b = new Number(1); // 引用值b
var c = a; // 把a的引用赋值给c
alert(a==b); // 返回false
alert(a==c); // 返回true
var s = "abc"; // 字符串,值类型数据
var o = new String(s); // 字符串对象,被装箱后的字符串
function f(v){ // 运算函数
v.toString = function(){ // 修改参数的方法toString()
return 123;
};
}
f(s); // 传入值
alert(s); // 返回字符串"abc",说明运算没有对原数据造成影响
f(o); // 传入引用
alert(o); // 返回数值123,说明运算已经影响到原数据的内部结构