JS执行原理
ECMA5时期与执行原理相关的术语
查看
JS引擎内部有一个执行上下文栈(Execution Context Stack
,简称ECS),它是用于执行代码的调用栈。
其首先执行的为全局代码块,即Global Execution Context(GEC)
全局执行上下文。

在全局代码块执行前,或者说在parse转为AST过程中,会创建一个GO(Global Object)全局对象,将全局定义的变量、函数等加入到GO中,但此时仅仅为创建,而不是赋值,即此时除了函数和内置对象(String、Data、parseInt等),GO中的变量值为undefined。

每一个执行上下文都会关联一个VO(Variable Object
)变量对象,其对应的this和作用域链(scope chain
)。
每当执行一个函数时,会创建一个函数执行上下文(Function Execution Context
,简称FEC)加入到ECS中,并在堆内存中创建一个AO(Activation Object
)活动对象,该AO会使用arguments作为初始化,初始值为该函数传入的参数形成的伪数组对象,此时VO指向该AO,存储该执行上下文变量的初始化。

在全局执行上下文下(即全局代码被执行时)VO就是GO(Global Object
)全局对象

当一个EC执行完后,会从ECS中弹出,与其VO关联的AO也会通过垃圾回收器清除。
每一个EC都会对应一个专属的AO,即使是同一个函数。
当代码被解析时,函数会被创建,但不会被执行,此时函数对象中会存在[[Scopes]]
属性,其值为一个列表对象,即为函数该的作用域链(scope chain),即函数的作用域链在定义的时刻就被创建,与其调用位置无关,与定义位置有关,当函数被调用,其成为FEC加入ECS中时,会创建scope chain
,并将函数对象中[[Scopes]]
属性的值赋值给FES中的scope chain
,当执行过程中遇到属性调用,会先在此FEC关联的VO中查找,查找不到,便会通过scope chain
向上查找。

ECMA5之后与执行原理相关的术语
查看
与ECMA5之前的思路基本相同,只是对于一些词汇的描述发生了改变。
在新的ECMA代码执行描述中,引入了词法环境(Lexical Environments)的概念。
-
一个词法环境是由环境记录(Environment Record,此后称ER)和一个外部词法环境(Outer Lexical Environment,此后称outer)组成。
-
一个词法环境经常用于关联函数声明、代码块语句、try-catch语句,当他们代码被执行时,词法环境被创建出来。

也就是说,当一个执行上下文(EC)加入到ECS中时,会关联对应的词法环境,通常会关联LexicalEnvironment
和VariableEnvironment
这两个词法环境。

通常LexicalEnvironment和VariableEnvironment指向同一个对象,但此要看浏览器怎么实现了。
LexicalEnvironment通常用于处理ES6新提出的let,const声明的标识符。

而VariableEnvirable用于处理var和function声明的标识符,与作用域提升有关。

对于ER来说,有两种主要的环境记录值:声明式环境记录和对象环境记录。
-
声明式环境记录:声明性环境记录用于定义ECMAScript语言语法元素的效果,如函数声明、变量声明和直接将标识符绑定与
ECMAScript语言值关联起来的Catch子句。 -
对象式环境记录:对象环境记录用于定义ECMAScript元素的效果,例如WithStatement,它将标识符绑定与某些对象的属性关联起来。

而在新的代码执行描述符中,对于GEC来说,其词法环境中的环境记录指向的并不仅仅式window,而是GER(Global Environment Record)和DER(Decalared Environment Record)的组合,GER指向的便是window,而DER指向的便是存储let,const等的环境记录对象。
且在ES6中,对于let,const所处的代码块同样存在作用域(块级作用域)。
总结一下:
ECMAScript5之后由于引入了块级作用域导致js执行出现了一定的变更,其相关术语也进行了改变,以适应新增变化,以下我对其js执行过程进行阐述,js引擎内部存在ECS(Execute Context Stack执行上下文栈)用于处理js代码的执行,在执行之前,会对代码进行全局解析,为执行代码提供环境,ECS中首先入栈的为GEC(Global Execute Context全局执行上下文),这是js代码执行的开始,在代码解析阶段,GEC中会关联词法环境,词法环境包含LexicalEnvironmnet和VariableEnvironment,最初LexicalEnvironmnet和VariableEnvironment指向同一个环境,一个词法环境由环境记录(Environment Record)和外部词法环境(Outer LexicalEnvironment)组成,环境记录主要分为声明式环境记录(处理let,const,函数声明等)和对象式环境记录(用于全局对象和with函数的情形),外部词法环境理解为作用域即可,在解析全局代码时,如既有var、let、const声明和函数定义,会将var和函数放入到VariableEnvironment的环境中,将let、const声明的属性放入到LexicalEnvironment中,注意,LexicalEnvironmnet和VariableEnvironment指向同一个环境此时仍指向同一个环境,既它们都添加到同一个环境记录上,但是会进行逻辑隔离,在GEC中,var声明的变量和函数会挂载到window上,所以在GEC的词法环境的环境记录中包括声明式环境记录和对象式环境记录,对象式环境记录为全局对象,而声明式环境记录存放let、const声明的变量,此时两者不仅在在逻辑上是隔离的,在物理上也是隔离的,此时由于处于GEC中,所以outer的值便为null,继续执行代码,当遇到函数调用时,会创建一个EC加入到ECS中,此EC的词法环境同样包含LexicalEnvironmnet和VariableEnvironment,他们初始指向的环境仍然相同,解析函数内代码时,发现既存在var、let、const声明和函数定义,仍然会将var和函数放入到VariableEnvironment的环境中,将let、const声明的属性放入到LexicalEnvironment中,但此时LexicalEnvironmnet和VariableEnvironment仍指向同一个环境,且此时是FEC中,此时他们词法环境的环境记录便指向同一个对象(不同的浏览器可能也会进行物理隔离),但进行逻辑隔离,此时outer指向为GEC的词法环境,如果执行过程中发现代码块(并不会创建一个新的执行上下文,执行上下文核心包括LexicalEnvironmnet、VariableEnvironment和this绑定,块级作用域中既无VariableEnvironment也无this),代码块中仍存在var、let、const声明,由于var并不存在块级作用域,所以var声明的变量会放到VariableEnvironment中,但对于let、const,会将FEC的LexicalEnvironmnet指向一个新的环境,然后将其变量放到新的环境的环境记录中,此时LexicalEnvironmnet和VariableEnvironment指向不同的环境,而此时新的词法环境中的outer便指向之前的LexicalEnvironmnet,以便形成作用域链,当代码块中的代码执行完,便将FEC的LexicalEnvironmnet重新指向原来的环境,以此类推,直至代码执行完毕。