代码如下:
var x = 1;
function A(y){
var x = 2;
function B(z){
console.log(x+y+z);
}
return B;
}
var C = A(1);
C(1);
分析如下:
阶段一:全局初始化阶段
js引擎在进入一段可执行代码时,要完成以下三个初始化工作:
- 创建一个全局对象
- 构建一个执行环境栈,与此同时创建一个全局执行环境并压入执行环境栈中
- 创建一个与全局执行环境相关的变量对象,此变量对象不仅包含全局对象中的所有属性,还包含全局定义的变量x和函数A。
阶段二:执行函数A
当执行函数A(1)时,js引擎要完成以下三个工作:
- 创建函数A的执行环境,并将A的执行环境推入执行环境栈顶并获取执行权限。
- 创建函数A的作用域链,js中每个函数执行时都会创建自己的执行环境,每个执行环境都有自己的作用域链,当执行环境被创建时,其作用域链初始化为当前函数的scope所包含的对象,即当前函数的作用域对象,而函数的scope是在函数定义时确定的,初始化为函数定义时所处环境的变量对象。
- 创建函数A执行环境的变量对象(也叫活动对象),此对象包含函数的形参、arguments对象、this对象以及内部变量和内部函数的定义,然后将此变量对象推入函数A作用域链顶端。
阶段三:执行函数B
函数A被执行以后,返回了B的引用,并赋值给了变量C,执行 C(1) 就相当于执行B(1),JS引擎需要完成以下工作:
- 创建函数B的执行环境,并将B的执行环境推入执行环境栈顶并获取执行权限。(注意:当函数A返回后,A的执行环境就会从栈中被删除,只留下全局执行环境)
- 创建函数B的作用域链,函数B是在函数A中定义的,函数B的作用域链初始化为执行环境A的变量对象。
- 创建函数B执行环境的变量对象(活动对象),并将此变量对象推入函数B作用域链的顶端。
当函数B执行“x+y+z”时,需要对x、y、z 三个标识符进行一一解析,解析过程遵守变量查找规则:先查找自己的变量对象(活动对象)中是否存在该属性,如果存在,则停止查找并返回;如果不存在,继续沿着其作用域链从顶端依次查找,直到找到为止,如果整个作用域链上都未找到该变量,则返回“undefined”。
函数B的作用域链为:
B的变量对象----->A的变量对象----->全局变量对象
因此,变量x会在A的变量对象中找到,y也会在A的变量对象中找到,z在自己的变量对象中找到 ,结果为2+ 1+ 1 = 4;
总结:
- 函数的scope是在定义时确定的。
- 函数的作用域链是在执行时确定的。
- 函数执行时首先会创建执行环境,然后创建函数的作用域链,接着创建函数的活动对象。
参考博客地址:http://www.cnblogs.com/onepixel/p/5090799.html(强烈推荐)