JavaScript 变量松散类型的本质,决定了它只是在特定时间用于保存特定值的一个名字而已。由于不存在定义某个变量要保存何种数据类型值的规则,变量的值及其数据类型可以在脚本的生命周期内改变。
基本类型和引用类型的值
ECMAScript 变量可能包含两种不同数据类型的值:基本类型值和引用类型值。
基本类型值指的是简单的数据段。
引用类型值指那些可能有多个值构成的对象。
在将一个值赋给变量时,解析器必须确定这个值是基本类型值,还是引用类型值。
ECMASScript 中有5种基本数据类型: Undefined
、Null
、 Number
、 String
、 Boolean
。这5种基本数据类型是按值访问的,因为可以操作保存在变量中的实际的值。
引用类型的值是保存在内存中的对象。与其它语言不同,JavaScript不允许直接访问内存中的位置,也就是说不能直接操作对象的内存空间。在操作对象是,实际上时钟操作对象的引用而不是实际的对象。为此,引用类型的值是按引用访问的。(当复制保存着对象的某个变量时,操作的是对象的引用。但在为对象添加属性时,操作的是实际的对象。)
1. 动态的属性
对基本类型值和引用类型值可以执行的操作大相径庭。
对于引用类型的值,我们可以为其添加属性和方法,也可以改变和删除其属性和方法。例如:
var person = new Object();
person.name = "Icey";
alert(person.name); //"Icey"
但是我们不能给基本类型的值添加属性,尽管这样做不会导致错误。例如:
var name = "Icey";
name.age = 27;
alert(name.age); //undefined
只能给引用类型值动态的添加属性,以便将来使用。
2. 复制变量值
除了保存的方式不同之外,从一个变量向另一个变量赋值基本类型值和引用类型值时,也存在不同。如果从一个变量向另一个变量复制基本类型的值,会在变量对象上创建一个新值,然后把该值复制到为新变量分配的位置上。例如:
var num1 = 5;
var num2 = num1;
num2中的5和num1中的5是完全独立的,num2中的5只是num1中5的一个副本。这两个变量可以参与任何操作而不会相互影响。
当从一个变量向另一个变量复制引用类型的值时,同样也会将存储在变量对象中的值复制一份放到为新变量分配的空间中。不同的是,这个值的副本实际上是一个指针,而这个指针指向存储在堆中的一个对象。复制操作结束后,两个变量实际上将引用同一个对象。因此,改变其中一个变量,就会影响另一个变量。例如:
var obj1 = new Object();
var obj2 = obj1;
obj1.name = "Icey";
alert(obj2.name); //"Icey"
3. 传递参数
ECMAScript 中所有函数的参数都是按值传递的。就是说,把函数外部的值复制给函数内部的参数,就和把值从一个变量复制到另一个变量一样。
在想参数传递基本类型的值时,被传递的值会被复制给一个局部变量(即命名参数,或者说是arguments对象中的一个元素)。在向参数传递引用类型的值时,会把这个值在内存中的地址赋值给一个局部变量,因此这个局部变量的变化会反映在函数的外部。例如:
function addTen(num) {
num += 10;
return 10;
}
var count = 20;
var result = addTen(count);
alert(count); //20,没变化
alert(result); //30
function setName(obj) {
obj.name = "Icey";
}
var person = new Object();
setName(person);
alert(person/name); //"Icey"
function setName(obj) {
obj.name = "Icey";
obj = new Object();
obj.name = "Root";
}
var person = new Object();
setName(person);
alert(person.name); //"Icey"
这个例子把person传递给setName()后,其name属性被设置为"Icey"。然后,又将一个想对象赋值个变量obj,同时将其name属性设置为"Root"。如果person是按引用传递的,那么person就会自动被修改为指向其name属性值为"Root"的新对象。但是,当接下来在访问person.name时,显示的值任然是"Icey"。这说明即使在函数内部修改了参数的值,但原始的引用任然保持未变。实际上,当在函数内部重写obj时,这个变量的引用就是一个局部对象了。而这个局部对象会在函数执行完毕后立即被销毁。
4. 检测类型
ECMAScript提供了instanceof
操作符,语法如下
result = variable instanceof constructor
如果变量是给定引用类型的实例,那么instanceof操作符就会返回true。
alert(person instanceof Object);
alert(colors instanceof Array);
alert(pattern instanceof RegExp);
根据规定,所有引用类型的值都是Object的实例,因此,在检测一个引用类型值和Object构造函数时,instanceof操作符始终会返回true。如果用instanceof操作符检测基本类型的值,始终会返回false。
- 基本类型值在内存中占据固定大小的空间,因此被保存在栈内存中
- 从一个变量向另一个变量复制基本类型的值,会创建这个值的一个副本
- 引用类型的是值对象,保存在堆内存中;
- 包含引用类型值的变量实际上包含的并不是对象本身,而是一个指向该对象的指针
- 从一个变量向另一个变量复制引用类型的值,复制的其实是指针,因此两个变量最终都指向同一个对象
- 确定ige值是哪种基本类型可以使用typeof操作符,而确定一个值是哪种引用类型可以使用instanceof操作符。