题目如下:
var x = 10;
function fn(){
console.log(x);
}
function show(f){
var x = 20;
(function () {
f();
})();
}
show(fn);
从题目可以看出,这里在全局和函数内部声明了两个同名的x变量。函数show内部还有一个立即执行函数。其实这些都是烟雾弹,这道题其实是主要想考查,我们对于作用域链的理解,下面我们来分析下:
1.首先在全局范围内,声明了两个函数 fn,show和一个x变量,全局环境执行,导致fn的定义,定义后,函数fn的作用域链上就拿到了父环境的作用域链,简单点说,fn现在的作用域链上只有一个全局global对象,而global对象上只有两个函数,一个x变量。
2.声明阶段完成后,在全局环境下,调用了show这个函数,并且把函数fn这个函数的引用当成实参传进了show函数体内。
3.show函数体内,同样声明了一个x变量,但是这个x其实是添加到show函数的作用域上,还声明了一个立即执行函数,在立即执行函数中,执行了fn的引用。fn执行会生成自己的活动对象(Active Object),并将自己的活动对象放在作用域的第一位。这个时候fn函数的作用域链就是AO(fn)-->GO(Global Obect)。
4.函数执行时,查找作用链上是否有访问的变量,都是顺着作用域链查起,也就是说从自己的AO查起,到GO结束。当然有些特殊用法,也有意外,比如with的用法。从上面的例子可以看出,函数fn的作用域链和show的作用域链基本上没关系。所以也就访问不到show内部声明的变量x,fn打印的其实就是全局声明的x。
5.最后说明一下,这里放在立即执行函数内,只是一个烟雾弹,删掉立即执行函数也不会改变fn的作用域的,打印的结果同样是10。
var x = 10;
function fn(){
console.log(x);
}
function show(f){
var x = 20;
f();
}
show(fn);
6.最后,再佐证下,fn和show的作用域没关系。这里在show内声明了一个m,如果fn和show的作用域有关系,打印m,应该可以打印出10,事实上没有找到m。
var x = 10;
function fn(){
console.log(m);
}
function show(f){
var x = 20;
var m = 10;
(function () {
f(m);
})();
}
show(fn);
结果: