javascript引擎在执行每个函数实例时,都会创建一个执行环境(execution context)。执行环境中包含一个调用对象(call object), 调用对象是一个scriptObject结构(scriptObject是与函数相关的一套静态系统,与函数实例的生命周期保持一致),用来保存内部变量表varDecls、内嵌函数表funDecls、父级引用列表upvalue等语法分析结构(注意varDecls和funDecls等信息是在语法分析阶段就已经得到,并保存在语法树中。函数实例执行时,会将这些信息从语法树复制到scriptObject上)。
b、javascript作用域机制的实现方法
词法作用域(lexical scope)是javascript的作用域机制,还需要理解它的实现方法,就是作用域链(scope chain)。作用域链是一个name lookup机制,首先在当前执行环境的scriptObject中寻找,没找到,则顺着upvalue到父scriptObject中寻找,一直lookup到全局调用对象(global object)。
现在回过头来分析第二个问题:
var arg = 1; function funcTest() { alert(arg); var arg = 2; } arg = 10; funcTest();
在执行funcTest函数时,也即进入了funcTest对应的作用域,js引擎在执行时,当遇到对变量名或者函数名的使用时,会首先在当前作用域(也即funcTest对应的作用域)查找变量或者函数(显然,arg变量在funcTest对应的作用域里被定义为var arg=2 所以alert方法的参数采用的是当前作用域的arg,但是因为arg被定义在alert方法后,所以arg变量默认值为undefined)。当然,如果没有找到就到上层作用域查找,依此类推(作用域范围可以持续到javascript运行环境的根:window对象)。
最后,让你看的更清楚,上面的代码其实可以等价于:
var arg = 1; function funcTest() { var arg; //默认值undefined alert(arg); arg = 2; } arg = 10; funcTest();
c、闭包(closure)
当一个函数实例执行时,会创建或关联到一个闭包。 (关于闭包,打算另写一篇学习笔记)
scriptObject用来静态保存与函数相关的变量表,闭包则在执行期动态保存这些变量表及其运行值;
闭包的生命周期有可能比函数实例长。函数实例在活动引用为空后会自动销毁;
闭包则要等要数据引用为空后,由javascript引擎回收(有些情况下不会自动回收,就导致了内存泄漏)。
ps:关于“执行过程”这一段比较拗口,名词很多,不过别被它们吓住,一旦理解了执行环境(execution context)、调用对象(call object)、词法作用域(lexical scope)、作用域链(scope chain)、闭包(closure)等这些概念,javascript的很多现象都能迎刃而解。
三、结语
通过第二段的分析,对照第一段笔者曾经做出的判断(你是不是也觉得笔者曾经的分析和结论很幼稚(哪怕有时结果碰巧也对!)?!不是一般的肤浅啊,^_^),你会发现原来javascript还有这么多“玄机”,而要真正理解精通又谈何容易?先“悟透”再说吧。