从前面的学习中,我们可以感觉到,JavaScript的变量与其他语言的变量有很大区别。JavaScript变量松散类型的本质,决定了它只是在特定时间用于保存特定值的一个名字而已。由于不存在定义某个变量必须保存何种数据类型值的规则,变量的值以及其数据类型可以在脚本的生命周期内改变。
- 基本类型和引用类型的值
ECMAScript变量可能包含两种数据类型的值:基本类型值和引用类型值。基本类型值指的是简单的数据段,而引用类型指那些可能由多个值构成的对象。基本数据类型是按值访问的,因为可以操作保存在变量中的实际的值。操作对象时,实际上是操作对象的引用而不是实际的对象。引用类型的值是按引用访问的。
1.1 动态的属性
对于引用类型的值,可以为其添加属性和方法,也可以改变和删除其属性和方法。例如:
<script>
var person=new Object();
person.name="JiaHua Chen";
alert(person.name);
</script>
很明显,并不能给基本类型的值添加属性,尽管这样做不会导致任何错误。例如:
<script>
var person=new Object();
person.age=27;
alert(person.age);
</script>
执行后发现,该属性值并没有不见。(→_→)
1.2 复制变量值
除了上述的保存方式的不同,从一个变量向另一个变量复制基本类型值和引用类型值时,也存在不同。从一个变量向另一个变量复制基本类型的值,会在变量对象上创建一个新值,然后将该值复制到为新变量分配的位置上。例如:
<script>
var num1=10;
var num2=num1;
document.write("num1="+num1+",num2="+num2+"<br>");
num2=20;
document.write("num1="+num1+",num2="+num2+"<br>");
</script>
num2是num1的一个副本,可以看到复制的num2和num1是完全独立的,互不影响。
当从一个变量向另一个变量复制引用类型的值时,同样也会将存储在变量对象的值复制放到新变量分配的空间中。该值的副本实际上是一个指针,而这个指针指向存储在堆中的一个对象。两个变量实际上引用同一个对象。例如:
<script>
var person=new Object();
person.name="喜剧演员";
var person2=person;
document.writeln("Person2 Name:"+person2.name+"<br>");
person.name="追光者";
document.writeln("Person2 Name:"+person2.name+"<br>");
</script>
person和person2两个变量指向同一个对象,所以person的name属性值改变之后,person2的name属性也随之改变。
1.3 传递参数
ECMAScript中所有函数的参数都是按值传递的。也就是把函数外部的值复制给函数内部的参数,就和把值从一个变量复制到另一个变量一样。访问变量有按值和按引用两种方式,而参数只能按值传递。在向参数传递基本类型的值时,被传递的值会被复制给一个局部变量;在向参数传递引用类型的值时,会把这个值在内存中的地址复制给一个局部变量,因此这个局部变量的变化会反映在函数的外部。
<script>
var num=20;
function add(num) {
num+=10;
return num;
}
var result=add(num);
document.write("num="+num+",result="+result+"<br>");
</script>
可以看到在调用add()函数之后,num的值并没有发生变化,可以说明的是值传递。
如果向参数传递的是引用类型的变量的话:
<script>
var person=new Object();
person.name="喜剧演员";
function changeName(obj) {
obj.name="追光者";
}
changeName(person);
document.write("Name:"+person.name);
</script>
发现在调用changeName()函数之后,发现person的name属性的值变化了。有种 错误的认知:在局部作用域中修改的对象会在全局作用域中反映出来,就说明参数是按引用传递的。为了证明对象是按值传递的,需要看一下面的例子:
<script>
var person=new Object();
function changeName(obj) {
obj.name="追光者";
obj=new Object();
obj.name="喜剧演员"
}
changeName(person);
document.write("Name:"+person.name);
</script>
发现打印出来的仍然是“追光者”。这说明即使在函数内部修改了参数的值,但原始的引用仍然保持不变。实际上,当在函数内部重写obj时,这个变量引用的就是一个局部对象了。而这个局部对象会在函数执行完毕之后立即被销毁。
1.4 检测类型
使用typeof操作符去检测变量是基本类型还是引用类型。
<script>
var s="AAAa";
var b=true;
var i=22;
var u;
var o=new Object();
var n=null;
document.write(typeof s+"<br>");
document.write(typeof b+"<br>");
document.write(typeof i+"<br>");
document.write(typeof u+"<br>");
document.write(typeof o+"<br>");
document.write(typeof n+"<br>");
</script>
u的类型的undefined,n和o的类型为object。
虽然在检测基本数据类型时,typeof非常好用,但是在检测引用类型的值时,这个操作符的作用不大。通常,我们想知道的是某个值是什么类型的对象而不是知道它是个对象,为此需要使用instanceof操作符。如果变量是给定引用类型的实例,那么就会返回true。
<script>
var array=new Array("AASA","ASas");
var o=new Object();
var reg=new RegExp("AA","SA");
alert(array instanceof Array);
alert(o instanceof Object);
alert(reg instanceof RegExp);
</script>