javascript中有六种数据类型,这六种类型可以分为两大类:基本数据类型和引用类型。基本数据类型包括:undefined,number,string,boolean。引用类型是:object,其实null也是object类型的一种特殊形式。可能有点奇怪的是string类型竟然也是基本数据类型。
这两大类型数据的最大区别就是在内存中的存储位置,基本数据类型都是保存在内存栈中的,因为在定义变量以后,javascript引擎就可以获取这种数据类型的长度,可以在栈内分配固定长度的内存保存这种数据类型。引用类型则是在栈中保存了一个指针,实际的数据是在指针指向的堆内存中保存,由于引用类型的数据长度是不固定的,而且可能随着程序的运行,数据大小是变化的,所以使用堆来保存这种数据类型比较合适。常见的引用类型(object衍生出来的)有数组(Array),日期类型(Date),正则表达式类型(Regexp)等。
我们使用typeof语句可以判断用户是否是六中基本类型,比如一个数组变量arrObj,使用这个表达式typeof arrObj==object返回的值是true。但是如果我们要判断是不是某个具体的引用类型,如某个变量是不是数组(Array)类型,就需要使用instanceof语句,例如arrObj instanceof Array。
在javascript中,变量作用域有点复杂,各个浏览器也有一定的差别,但是大体上还是遵循了ECMA-262标准的。下面就是各种变量作用域的例子
全局作用域,下面的变量就是全局作用域。
<script type="text/javascript">
var name="Michael";
</script>
局部作用域
<script type="text/javascript">
function message(){
var msg="welcome to my blog";
}
</script>
上面两种只是比较明显的两种变量的作用域,在进一步介绍作用域之前,需要先了解一下程序运行上下文(execution context)的概念。在javascript的程序中,每一个函数,变量都属于一个上下文中,在全局作用域中定义的变量(函数也是一种变量),它的上下文是window,所以上面的变量name和函数message,都是属于window这个上下文的。window是系统提供的一个上下文。msg的上下文是message函数,在函数中定义的变量都挂在了函数名称代表的上下文中。现在有了两个上下文了,这两个context有什么关系呢?这有涉及到了另外一个概念,上下文链。这个链的主要作用是搜索变量用的,下面的例子详细描述的这种关系
<script type="text/javascript">
var name="Michael";
function message(){
var msg="welcome to my blog";
function showMessage(){
var date="2013-11-13";
alert(name+" "+msg+" "+date);
};
showMessage();
}
</script>
上面的程序中涉及到三个上下文window->message->showMessage。其中在showMessage中用到了三个变量name,msg和date。在运行showMessage的使用,首先会在showMessage上下文中查找变量名,如果找不到,如msg,则向上一级的上下文中发出请求,找这个变量名,如果找到,这返回这个变量的值,否则继续向上一级发出请求,直到找到根上下文(window),如果没有找到,则返回一个错误。这就是上下文链的作用,这个链的寻找时单向的,只能下级向上级发起寻找,上级是不能向下级发起寻找请求的,如果我们在message函数中使用date变量会报错。
下面是两种特殊的情况:
1.javascript没有块级作用域,如下面的这种情况,变量msg虽然在if语句块中定义,但是在语句块之外,也可以访问msg变量。因为javascript没有块级作用域,所以在语句块(如for,while,try,with等)中定义的变量,会自动向上找到最近的上下文,并把变量挂到这个上下文上面。注意,catch块比较特殊,除了IE之外,在catch块中定义的变量,在catch块之外是不能访问的,如下面的第二个函数。
<script type="text/javascript">
function message(){
var bl=true;
if(bl){
var msg="test1";
}else
{
var msg="test2";
}
alert(msg);
}
function showError(){
try{
var msg="success";
}catch(e){
var errorMessage="failed";
}
alert(msg); //正常
alert(errorMessage); //除了IE,其他的浏览器都报错
}
</script>
1.没有var指令,直接赋值的变量,都是挂到window上下文中,而不是最近的上下文中,这也说明了var指令的作用。如下面的变量msg,并不是属于函数message,而是属于window对象。如果我们在第二函数showMessage中访问msg,是可以正常获取msg变量的值。但是vMsg却不可以在第二个函数中获取其中的值。
<script type="text/javascript">
function message(){
msg="test";
var vMsg="test";
}
function showMessage(){
alert(msg); //test
alter(vmsg);// 错误,不能执行
}
</script>
最后,再了解一个javascript中的垃圾回收器算法,以前有两种:一是标记清除(mark-and-clean),另一个是引用计数法。现在几乎所有的javascript实现都是使用标记清除法。
标记清除法是当变量超出作用域之后,就打上可以清除的标记(无论用什么方法),当进程空闲的时候就把这部分内容清除掉(或者当内存不足的时候)。由于javascript是嵌入式的语言,在浏览器中尤其明显,内存份额是有限的,所以垃圾回收器和程序的算法尤其重要。尽量不要定义全局变量,定义变量的时候要使用var指令。
引用计数法虽然在javascript语言中不再使用了,但是javascript运行环境中的html的dom对象的实现却还是使用了引用计数的方法作为垃圾回收器的算法,这也是一般windows系统中com组件的使用的算法。如果我们使用dom对象或者activex对象,尽量避免循环引用。如果不能避免,一定要显示的取消引用。如下面例子中的循环引用
<script type="text/javascript">
function message(){
var person=new Object();
person.name="test";
var element=document.getElementById("name");
person.input=element;
element.obj=person;
}
</script>