下面是摘抄自《JavaScript内核》这本书的。
在JavaScript中,在所有函数体之外声明的变量为全局变量,而在函数体内声明的变量(通过var关键字)为局部变量。事实上,全局变量是全局对象的属性而已,比如在客户端的JavaScript中,我们声明的变量其实是window对象的属性,如此而已。
那么,局部变量又隶属于什么对象呢?就是我们要讨论的调用对象。在执行一个函数时,函数的参数和其局部变量会作为调用对象的属性进行存储。同时,解释器会为函数创建一个执行器上下文(Execution Context),与上下文对应起来的是一个作用域链。顾名思义,作用域链是关于作用域的链,通常实现为一个链表,链表的每个项都是一个对象,在全局作用域中,该链中有且只有一个对象,即全局对象。对应的,在一个函数中,作用域链上会有两个对象,第一个(首先被访问到的)为调用对象,第二个为全局对象。
如果函数需要用到某个变量,则解释器会遍历作用域链,比如:
var str = "global";
function scopeTest(){
print(str);
var str = "local";
print(str);
}
当解释器进入scopeTest函数的时候,一个调用对象就被创建了,其中包含了str变量作为其中的一个属性并被初始化为undefined,当执行到第一个print(str)时,解释器会在作用域链中查找str,找到之后,打印其值为undefined,然后执行赋值语句,此时调用对象的属性str会被赋值为local,因此第二个print(str)语句会打印local。
应该注意的是,作用域链随着嵌套函数的层次会变的很长,但是查找变量的过程依旧是遍历作用域链(链表),一直向上查找,直到找出该值,如果遍历完作用域链仍然没有找到对应的属性,则返回undefined。