代码改变世界

透过Javascript一些变量定义及简单运算实验看JS内部运行机制

2013-04-07 20:16  MoltBoy  阅读(430)  评论(0编辑  收藏  举报

非常经典的几道面试题:

if(!("a" in window)){
    var a = true;
}
alert(a);

以上题目运行结果为“indefined",其实把”a“看做a是不是更容易理解。变量a在全局作用域定义,而全局作用域(global)变量都是window对象的属性,因而a in window自然为true,剩下的就不用解释了吧!另外,全局变量对象的声明:VO(global){a:undefined}。

举一反三类推,”a“ in top 或者 ”a“ in self呢,结果跟window是一样的,上诉运行环境中,top就是window,而self基本上可以跟window互换,这样就不难理解了吧!剩下试试a in document,相信大家都明白,显而易见,document跟a同属于window的属性,所以a in document肯定为false。

继续下一道题:

 var a = 1;
 var b = function a(x){
     x && a(--x);
 };
 alert(a);

运行的结果为”1“,这道题考验的是函数表达式以及函数声明的深入区别,题目中用的是函数表达式,后面却用了函数名,这里的函数名的作用域相当于形参,也就是说对外部的作用域没有影响,因此并不会跟全局作用域的a产生声明冲突。

var a = 1;
function a(x){
    x && a(--x);
}
alert(a);

若将函数表达式换成函数声明function a(x){x && a(--x);},将会出现非常有意思的现象。在这里,要补充一点营养品,否则会营养不良。Javascript无需编译,是解释性脚本语言(JS引擎未必是单纯的解释器,如:google的V8),但是在解释器(Interpreter)解释过程中,又分为Javascript预编译和语句执行两个阶段。而变量对象(variable object)声明和函数声明都是在预编译期间处理,而赋值和变量初始化都是在执行期间处理。

在预编译期间,JS解释器存在一个变量声明和函数声明的提升机制(variable declaration hoisting and function declaration hoisting),会将它们提升到变量运行的context中去,并赋值undefined,具体的内部操作过程:首先,创建一个当前执行环境下的活动对象(AO:active object),然后将用 var 声明的变量设置为活动对象的属性(也就是将其添加到活动对象当中)并将其赋值为undefined,然后将function 定义的函数也添加到活动对象当中。AO(context){variable:undefined,functionName:function(){}}。执行语句期间,解释器会给变量赋值初始化,将函数名变量指向函数体。总结就是一句话,函数声明在预编译期间静态建立,函数表达式在执行过程中动态内建(var声明的变量名除外)。

解释了一大堆,回到原话题,经过解释器的预编译,a会被声明成函数(function a(x){ x && a(--x); }),而在执行期间,a被初始化为1,所以结果还是1。

另外,还需要注意的是,javascript解释器是按块来进行预编译的,也即是按<script>标签来分块。