深入理解JS执行细节从Javascript的定义到执行,JS引擎在实现层做了大量的初始化工作,所以在学习JS引擎的工作机制之前,我们需要介绍几个相关概念:执行环境栈、全局对象、执行环境、变量对象、活动对象、作用域和作用域链等,这些概念是JS引擎工作的核心组成部分。本文的目的不是孤立地给大家讲解每一个概念,而是通过一个简单的DEMO来分析,讲解JS引擎从定义到执行的每一个细节,以及这些概念在其中所起到的作用。变量x=1;//定义一个全局变量xfunctionA(y){varx=2;//定义一个局部变量xfunctionB(z){//定义一个内部函数Bconsole.log(x+y+z);}返回B;//返回函数B的引用}varC=A(1);//执行A,返回BC(1);//执行函数B,输出4接下来我们对函数A和执行函数B进行全局初始化和执行,分三个阶段分析JS引擎的工作机制:1.全局初始化当JS引擎进入一段可执行代码时,需要完成以下三个初始化任务:首先,创建一个全局对象(GlobalObject),这个对象全局只有一个副本,它的属性可以在任何地方访问,它的存在伴随着应用程序的整个生命周期。创建全局对象时,常用的JS对象如Math、String、Date、document作为其属性。由于这个全局对象不能通过名字直接访问,所以还有一个属性window,window指向自己,这样就可以通过window访问这个全局对象了。用伪代码模拟全局对象的大致结构如下://创建一个全局对象varglobalObject={Math:{},String:{},Date:{},document:{},//DOM操作...window:this//让window属性指向自己}那么,JS引擎需要构建一个执行环境栈(ExecutionContextStack),同时,还要创建一个全局执行环境(ExecutionContext)EC,并将这个全局执行环境EC推送到执行环境栈上。执行环境栈的作用是保证程序能够按正确的顺序执行。在javascript中,每个函数都有自己的执行环境。当一个函数被执行时,该函数的执行环境会被压入执行环境栈顶,并获得执行权。当这个函数执行时,它的执行环境从栈顶删除,执行权还给之前的执行环境。我们用伪代码来模拟执行环境栈和EC的关系:varECStack=[];//定义一个执行环境栈,类似于数组varEC={};//创建一个执行空间,//ECMA-262规范没有明确定义EC的数据结构,可以理解为在内存中分配的一块空间ECStack.push(EC);//进入函数并推入执行环境ECStack.pop(EC);//函数返回最后,删除执行环境最后,JS引擎会创建一个与EC关联的全局变量对象(VaribaleObject)VO,并将VO指向全局对象。VO不仅包含了全局对象的原有属性,还包含了全局定义变量x和函数A,同时在定义函数A时,为A增加了一个内部属性scope,scope指向VO。当每个函数被定义时,它会创建一个与之关联的作用域属性,作用域总是指向定义该函数的环境。此时的ECStack结构如下:ECStack=[//执行环境栈EC(G)={//全局执行环境VO(G):{//定义全局变量对象...//包含原始属性全局对象x=1;//定义变量xA=function(){...};//定义函数AA[[scope]]=this;//定义A的作用域,赋值给VO本身}}];2.执行函数A当执行进入A(1)时,JS引擎需要完成以下工作:首先,JS引擎会创建函数A的执行环境EC,然后将EC推到执行环境的最顶层入栈并获得执行权。此时执行环境栈中有两个执行环境,分别是全局执行环境和函数A的执行环境。A的执行环境在栈顶,全局执行环境在栈底的堆栈。然后,创建函数A的作用域链(ScopeChain)。在javascript中,每个执行环境都有自己的作用域链,用于标识符解析。创建执行环境时,会初始化其作用域链。当前运行的函数范围内包含的对象。接下来,JS引擎会创建一个当前函数的激活对象(ActivationObject)AO,其中激活对象起到了变量对象的作用,只是在函数中调用方式不同(你可以把变量对象看成是一个一般概念,活动对象是它的一个分支),AO包含函数的形参,arguments对象,this对象,局部变量和内部函数的定义,然后AO会被推到最上面作用域链。需要注意的是,在定义函数B时,JS引擎还会为B添加一个scope属性,将作用域指向定义函数B的环境。函数B定义的环境是A的活动对象AO,AO位于链表的前端。因为链表具有端到端连接的特点,函数B的作用域指向了A的整个作用域链。我们看此时的ECStack结构:ECStack=[//执行环境栈EC(A)={//A的执行环境[scope]:VO(G),//VO是全局变量对象AO(A):{//创建函数A的活动对象y:1,x:2,//定义局部变量xB:function(){...},//定义函数BB[[scope]]=this;//this指的是AO本身,AO在scopeChain的最顶端,所以B[[scope]]指向整个作用域链arguments:[],//通常我们在函数中访问的参数都是AO中的参数this:window//函数中的this指向调用者窗口对象},scopeChain:
