首先需要明确一个执行环境的概念,执行环境这个概念是用来理解作用域的,在js中,执行环境分为全局执行环境和局部(function)执行环境,而在C#这类的C类语言中,还有一个块级别的执行环境,如if语句、for语句所形成的花括号包围起来的块。
然后就是需要理解一个关键字:var,var这个关键字在js中是用来创建一个局部变量的,使用var创建的局部变量会保存在最接近的作用域中。比如,在function中var person=new Object();就会在函数域中定义一个局部变量,这个局部变量会被放到function作用域链的前端。
执行环境:执行环境是js中最为重要的一个概念,它定义了变量或者函数有权访问的其他数据。执行环境都有一个变量对象与之进行对应,对于这一点,我的理解是因为js中没有像其他面向对象的语言可以来定义一个class,所以,在js中,执行环境对应的变量对象就是相当于在C#等语言中定义的一个class。而js中的执行环境都是固定的,比如在网页上面执行的话这个变量对象就是window(window class),在控制台上面执行的话就是console(console class)。更近一步的,在相应的执行环境中定义的属性和函数也都是作为这个执行环境的变量对象的属性和函数来进行保存的。
每个函数都有自己的执行环境(局部的),在js中存在一种这样的机制:当执行流进入一个函数中时,会将这个函数推入一个执行栈,而当函数执行完毕后会从执行栈弹出。利用这种机制来灵活的控制js的执行流。当代码在特定的环境中执行时,会创建变量对象的一个作用域链,这个作用域链的作用时保证代码的访问权限(有序访问)。作用域链的最前端始终都是当前环境的对象变量。如果这个环境是函数,那么在这个执行环境的变量对象的作用域链的最前端就是这个函数中的活动对象,活动对象最开始时只包含一个arguments。(argument是一个数组用来存放function所接受的实参。可以通过arguments[]来访问function所接受的所有实参。也可以用arguments.length来查看传入实参的个数。)作用域链上的下一个变量对象则是当前执行环境的包含环境,按照这样的排列逐渐扩大,最后到全局执行环境。
看一个例子:
var color = "blue"; function changeColor() { if (color === "blue") { color = "red"; } else { color = "blue"; } }
changeColor();
alert("color is now "+color);
函数changeColor()的作用域链上包含两个变量对象,自身的arguments变量对象以及全局的color。可以在内部访问到color,就是因为可以在作用域链上能够找到它。
此外,由于没有块级作用域的支持,可能会出现如下让人难以理解的例子:
var color = "blue"; function changeColor() { if (true) { var person="a"; } alert(person); } changeColor(); alert("color is now "+color);
在if条件语句中定义一个局部的变量,可以在if外面访问到!这是因为在function内部任何部位定义的变量是在function这个执行环境的变量对象的作用域链上对该变量进行了添加,所以在这个执行环境的任何地方都可以对这个变量进行访问。