JavaScript基本类型和引用类型的值
ECMAScript变量可能包含两种不同数据类型的值:基本类型值和引用类型值。基本类型值指的是简单的数据段,而引用类型值指那些可能由多个值构成的对象。
在将一个值赋给变量时,解析器必须确定这个值是基本类型值还是引用类型值。第3章讨论了5种基本数据类型:
Undefined
、Null
、Boolean
、Number
和String
。这5种基本数据类型是按值访问的,因为可以操作保存在变量中的实际的值。
引用类型的值是保存在内存中的对象。与其他语言不同,JavaScript不允许直接访问内存中的位置,也就是说不能直接操作对象的内存空间。在操作对象时,实际上是在操作对象的引用而不是实际的对象。为此,引用类型的值是按引用访问的1。
动态的属性
定义基本类型值和引用类型值的方式是类似的:创建一个变量并为该变量赋值。
但是当这个值保存到变量中以后,对不同类型值可以执行的操作是不同的。
我们可以为引用类型值添加属性和方法,也可以改变和删除。
var person = new Object();
person.name = 'John';
console.log(person.name) // 'John'
如果对象不被销毁或者这个属性不被删除,则这个属性将一直存在。
但是,不能为基本类型值添加属性
var name="John";
name.age = 18;
console.log(name.age) // undefined
为字符串·name
定义了一个名为age
的属性,并为该属性赋值27。但在下一行访问这个属性时,发现该属性不见了。这说明只能给引用类型值动态地添加属性,以便将来使用。
复制变量值
从一个变量向另一个变量复制基本类型值和引用类型值时,也存在不同。
基本类型值:
var num1 = 5;
var num2 = num1;
image.png
引用类型值:
var obj1 = new Object();
var obj2 = obj1;
obj1.name = "Nicholas";
alert(obj2.name); //"Nicholas"
image首先,变量
obj1
保存了一个对象的新实例。然后,这个值被复制到了obj2
中;换句话说,obj1
和obj2
都指向同一个对象。这样,当为obj1
添加name
属性后,可以通过obj2
来访问这个属性,因为这两个变量引用的都是同一个对象。
传递参数
基本类型值:
function addTen(num) {
num += 10;
return num;
}
var count = 20;
var result = addTen(count);
alert(count); //20,没有变化
alert(result); //30
引用类型值:
function setName(obj) {
obj.name = "Nicholas";
}
var person = new Object();
setName(person);
alert(person.name); // "John"
以上代码中创建一个对象,并将其保存在了变量
person
中。然后,这个变量被传递到setName()
函数中之后就被复制给了obj
。在这个函数内部,obj
和person
引用的是同一个对象。换句话说,即使这个变量是按值传递的,obj
也会按引用来访问同一个对象。于是,当在函数内部为obj
添加name
属性后,函数外部的person
也将有所反映;因为person
指向的对象在堆内存中只有一个,而且是全局对象。
注:有很多开发人员错误地认为:在局部作用域中修改的对象会在全局作用域中反映出来,就说明参数是按引用传递的。为了证明对象是按值传递的,我们再看一看下面这个经过修改的例子:
function setName(obj) {
obj.name = "John";
obj = new Object();
obj.name = "Greg";
}
var person = new Object();
setName(person);
alert(person.name); // "John"
这个例子与前一个例子的唯一区别,就是在
setName()
函数中添加了两行代码:一行代码为obj
重新定义了一个对象,另一行代码为该对象定义了一个带有不同值的name
属性。在把person
传递给setName()
后,其name属性被设置为"John"
。然后,又将一个新对象赋给变量obj
,同时将其name属性设置为"Greg"
。如果person
是按引用传递的,那么person
就会自动被修改为指向其name
属性值为"Greg"
的新对象。但是,当接下来再访问person.name
时,显示的值仍然是"John"
。这说明即使在函数内部修改了参数的值,但原始的引用仍然保持未变。实际上,当在函数内部重写obj
时,这个变量引用的就是一个局部对象了。而这个局部对象会在函数执行完毕后立即被销毁。
检测类型
typeof
操作符:
var s = "Nicholas";
var b = true;
var i = 22;
var u;
var n = null;
var o = new Object();
alert(typeof s); //string
alert(typeof i); //number
alert(typeof b); //boolean
alert(typeof u); //undefined
alert(typeof n); //object
alert(typeof o); //object
instanceof
操作符:
alert(person instanceof Object); // 变量person是Object吗?
alert(colors instanceof Array); // 变量colors是Array吗?
alert(pattern instanceof RegExp); // 变量pattern是RegExp吗?
根据规定,所有引用类型的值都是
Object
的实例。因此,在检测一个引用类型值和Object
构造函数时,instanceof
操作符始终会返回true
。当然,如果使用instanceof
操作符检测基本类型的值,则该操作符始终会返回false
,因为基本类型不是对象。