4.1基本类型和引用类型的值
ECMAScript变量可能包含两种不同数据类型的值:基本数据类型和引用数据类型。
基本类型值指的是简单的数据段,引用类型值指那些可能由多个值构成的对象。
5种基本数据类型:Undefined Null Boolean Number String 这5种基本数据类型是按值访问的,因为可以操作保存在变量中的实际的值。
引用类型的值是保存在内存中的对象,JavaScript不允许直接访问内存中的位置,也就是不能直接操作对象的内存空间,在操作对象时,实际上是在操作对象的引用而不是实际的对象,为此,引用类型的值是按引用访问的。
4.1.1动态属性
对于引用类型的值,我们可以为其添加属性和方法,也可以改变和删除此方法。
var person = new Object();
person.name = "zhangsan"; //添加名为name的属性并将"zhangsan"赋给这个属性,对象不被销毁或属性不被删除,这个属性将一直存在
alert(person.name);//zhangsan
我们不能给基本类型的值添加属性,尽管这样做不会导致任何错误。
var name = "zhangsan";
name.age = 18;
alert(name.age);//undefined
只能给引用类型动态的添加属性
4.1.2复制变量值
除了保存的方式不同之外,在从一个变量向另外一个变量复制基本数据类型和引用数据类型的时候,也存在不同,如果从一个变量向另一个变量复制基本类型的值,会在变量对象上创建一个新值,然后把该值复制到为新变量分配的位置上。
var num1 = 5;
var num2 = num1;
num1中保存的值是5,当使用num1的值来初始化num2时,num2中也保存了值5,但num2中的5与num1中的5是完全独立的,该值只是num1中5的一个副本,此后,这两个变量可以参与任何操作而不会互相影响。
当从一个变量向另一个变量复制引用类型的值时,同样也会将存储在变量对象中的值复制一份放到为新变量分配的空间中。不同的是,这个值的副本实际上是一个指针,而这个指针指向存储在堆中的一个对象。复制操作结束后,两个变量实际上将引用同一个对象,因此改变其中一个变量,就会影响另一个变量
var obj1 = new Object();
var obj2 = obj1;
obj1.name = "zhangsan";
alert(obj2.name);//zhangsan
首先,变量obj1保存了一个对象的新实例,然后这个值被复制到了obj2中,换句话说,obj1和obj2都指向同一个对象,这样当为obj1添加name属性后,可以通过obj2来访问这个属性,因为这两个变量引用的是同一个对象。
4.1.3传递参数
ECMAScript中所有函数的参数都是按值传递的
在向参数传递基本类型的值时,被传递的值会被赋值给一个局部变量。在向参数传递引用类型的值时,会把这个值在内存中的地址复制给一个局部变量,因此这个局部变量的变化会反应在函数的外部。
function add(num){
num += 10;
return num;
}
var count = 20;
var result = add(count);
alert(count);//20,没有发生变化
alert(result);//30
这里的函数add()有一个参数num,而参数实际上是函数的局部变量,在调用这个函数时,变量count作为参数被传递给函数,在函数内部,参数num的值被加上了10,但这一变化不会影响函数外部的count变量,参数num与变量count互不相识,它们仅仅具有相同的值。
function setName(obj){
obj.name = "zhangsan";
}
var person = new Object();
setName(person);
alert(person.name);//zhangsan
以上代码创建一个对象,并将其保存在变量person中,然后,这个变量被传递到setName()函数中之后就被复制给了obj,在这个函数内部,obj和person引用的是同一个对象,换句话说,即使这个变量是按值传递的,obj也会按引用来访问同一个对象。于是在函数内部为obj添加name属性后,函数外部的person也将有所反映,因为person指向的对象在堆内存中只有一个,而且是全局对象。
为了证明对象是按值传递的,看下面的例子:
function setName(obj){
obj.name = "zhangsan";
obj = new Object();
obj.name = "lisi";
}
var person = new Object();
setName(person);
alert(person.name);//zhangsan
这个例子与上一个例子的区别就是setName()函数中添加了两行代码:一行代码为obj重新定义了一个对象,另一行代码为该对象定义了一个带有不同值的name属性。在把person传递给setName()后,其name属性被设置为“zhangsan”,然后,又将一个新对象赋给变量obj,同时将其name属性设置为"lisi",如果person是按引用传递的,那么person就会被自动修改为指向其name属性值为"lisi"的新对象。但是,在接下来访问person.name时,显示的值仍然是“zhangsan”,这说明即使在函数内部修改了参数的值,但原始的引用仍然保持未变。实际上,当在函数内部重写obj时,这个变量引用的就是一个局部对象了,这个局部对象会在函数执行完毕后立即被销毁。
4.1.4检测类型
要检测一个变量是不是基本类型,用typeof可以确定一个变量是string,number,Boolean,undefined还是object。
在检测引用类型的时候,我们想知道变量是什么类型的对象时,用instanceof
如果变量是给定引用类型的实例,那么instanceof就会返回true,例如:
alert(person instanceof Object);
alert(colors instanceof Array);
alert(pattern instanceof RegExp);
根据规定,所有引用类型的值都是Object的实例,因此,在检测一个引用类型值和Object构造函数时,instanceof操作符始终会返回true,当然,如果使用instanceof操作符检测基本类型的值,则始终返回false,因为基本类型不是对象。