var x=10;
function a(){
console.log(x);
}
function b(){
var x=20;
a();
}
a();//10
b();//还是10;
看到这里时,我的内心是崩溃的,深知自己有太多的基础要补充。
执行上下文(Execution Context)
一个执行的上下文可以抽象的理解为object。每个执行上下文都有一系列属性。
![上下文结构]](http://upload-images.jianshu.io/upload_images/1512918-850602131231d127.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
&变量对象Variable Object
是与执行上下文相关的数据作用域,存储被定义在上下文中的变量和函数声明。
var foo = 10;
function bar() {console.log("bar")} // // 函数声明
(function run() {console.log("run")}); // 函数表达式
console.log(
this.foo == foo, // true
window.bar == bar // true
);
bar();//bar
run();//报错,run is not defined;
全局上下文中的变量对象VO有属性
仅有函数能创建新的作用域。使用eval时也会使用一个新的执行上下文(会只用全局变量对象或调用者的变量对象)
&活动对象Activation Object
函数被调用时,活动对象被创建,在函数上下文中作为变量对象使用。
函数的变量对象不变,除了存储变量和函数声明,还有特殊对象arguments。
函数表达式同样不在AO中。
&作用域链
一般情况下,一个作用域链包括父级变量对象(作用域链顶部),函数自身变量VO,活动对象AO。查找标识符时,自己作用域中没有,就找父级的。
回到这个例子:
var x=10;
function a(){
console.log(x);
}
function b(){
var x=20;
a();
}
b();
1、JS是静态作用域链,函数执行前,全局环境就关联了一个作用域链,变量对象包含x,a,b。a,b函数也各自关联自己的作用域链。
globalScopeChain={
x:10,
a:[function],
b:[function]
};
aSchopeChain=[globleSchipeChain]
bSchopeChain=[globleSchipeChain]
2、调用b函数,进入b的局部执行环境,创建活动对象包含arguments和x,并将活动对象加入到作用域链的前端;
bSchopeChain=[
{
arguments:[],
x:20
},
{
x:10;
a:[function],
b:[funciton]
}
3、调用a函数,进入a的局部执行环境,创建活动对象包含arguments,并将活动对象添加到作用域链前端。
aSchopeChain=[
{
arguments:[]
},
{
x:10,
a:[funciton],
b:[function]
}
]
会沿着自己的作用域链查找x,直到作用域链顶端。也就可以理解为啥是这样的结果了。
总结
简而言之,b与b函数中的a函数,两者仅仅是调用关系,因此对于作用域链上两者没有任何关系,虽然是进入b函数后调用a,但其实就是window.a()。
参考资料:
http://www.cnblogs.com/TomXu/archive/2012/01/18/2312463.html
http://www.cnblogs.com/TomXu/archive/2012/01/12/2308594.html