在JavaScript代码中全局区域声明的变量是全局变量,在函数体内声明的变量是局部变量,全局变量具有全局作用域,在整个JS代码中(包括函数内)都可以被访问,局部变量只可以在函数体内被访问,函数之外的代码无法访问。
在JavaScript代码中全局区域代码有全局上下文,函数调用时会有对应的函数上下文。执行上下文包含变量对象,作用域链以及this值等等其他属性。
一、变量对象和函数的[[Scope]]属性
执行上下文的变量对象保存本上下文的变量,函数声明和函数参数。全局上下文保存全局变量,函数上下文保存函数中的局部变量。
var x = 10;
function foo() {
var y = 20;
alert(x + y);
}
foo();//30
上面代码中全局变量对象包含变量x,函数声明foo;函数foo调用时的函数上下文中变量对象包含变量y。由于两个执行变量之间无相互访问的关系,函数foo执行时如何访问变量x的呢?就需要借由函数的属性[[Scope]]。
[[Scope]] is a hierarchical chain of all parent variable objects, which are above the current function context; the chain is saved to the function at its creation.
[[Scope]]是所有父类变量对象的一个层次链,级别高于当前函数的上下文,在函数创建时存在函数中。
上述代码的[[Scope]]属性为:
foo.[[Scope]] = [
globalContext.VO // === Global
];
函数的[[Scope]]属性是在函数声明定义时存储到函数的,函数一旦创建就会一直存在,直到函数被销毁。[[Scope]]存储的当前函数的所有父级的变量对象。
二、作用域链
执行上下文中的作用域链定义如下:
ScopeChain = AO|VO + [[Scope]]
VO指变量对象(Variable Object),函数上下文中的变量对象用AO活动对象(activation object)表示。
函数执行的过程中查询变量所属变量对象范围时先从函数的局部变量开始查找(VO|AO),若查找不到再从[[Scope]]中查找。
var x = 10;
function foo() {
var y = 20;
function bar() {
var z = 30;
alert(x + y + z);
}
bar();
}
foo(); // 60
这段代码中全局上下文的变量对象为
globalContext.VO === Global = {
x: 10
foo: <reference to function>
};
其中函数foo声明时[[Scope]]属性为
foo.[[Scope]] = [
globalContext.VO
];
当调用foo函数时,进入函数foo调用实例的上下文,函数上下文的变量对象为
fooContext.AO = {
y: 20,
bar: <reference to function>
};
此时foo函数上下文的作用域链为
fooContext.ScopeChain = fooContext.AO + foo.[[Scope]] // i.e.:
fooContext.ScopeChain = [
fooContext.AO,
globalContext.VO
];
内部函数bar创建时,函数bar的[[Scope]]属性为
bar.[[Scope]] = [
fooContext.AO,
globalContext.VO
];
在bar函数调用时,函数的变量对象为
barContext.AO = {
z: 30
};
作用域链为
barContext.ScopeChain = barContext.AO + bar.[[Scope]] // i.e.:
barContext.ScopeChain = [
barContext.AO,
fooContext.AO,
globalContext.VO
];
因此在对上述代码中三个变量的解析过程为:
在barContext.ScopeChain中分别解析x,y,z三个变量,首先查找 barContext.AO中,x,y查不到,z为30;
然后继续从fooContext.AO中查找x,y这两个变量,x查不到,y为20;
之后从globalContext.VO中查找x,结果为10。
//查找"x"
barContext.AO // not found
fooContext.AO // not found
globalContext.VO // found - 10
//查找"y"
barContext.AO // not found
fooContext.AO // found - 20
//查找"z"
barContext.AO // found - 30