JS 执行机制

        JS代码通常是自上而下逐行执行,执行方式称为单线程执行模型。然而,变量和函数往往在定义之前就可以使用,我们知道这种现象被称为变量提升。那么为什么会出现变量提升呢? JS的执行机制还需要更深入了解下。

变量提升

        从字面意思上变量声明和函数声明会被提升到代码最前面,实际其在代码的位置并未改变,实际上JS代码执行机制是先编译再执行,在执行之前需要先编译。

在编译阶段,变量和函数会被存放到变量环境(内存)中;

执行阶段,JS引擎会从变量环境中查找自定义的变量和函数。具体过程如下:

编译阶段

       代码编译会生成两部分内容:执行上下文(Execution context)和可执行代码。其中执行上下文是JS执行一段代码的运行环境,确定函数执行期间的this、变量、对象以及函数等;执行上下文中存在一个变量环境对象(Viriable Environment),该对象存放了变量提升的内容,如图

JS 执行机制_第1张图片

        变量提升只提升声明(left hand)不提升赋值(right hand),也就是说JS引擎会将会在变量环境对象(Viriable Environment)中,为var声明的变量创建对应属性,并设置默认值为undefined。 函数的声明主要有: 函数声明和函数表达式;其中函数声明会将方法体也提升,JS引擎会将方法体存储在堆中,变量环境对象中创建对应属性,属性值指向堆中函数的位置;而函数表达式同变量提升一样,只会提升声明;

当代码中出现同名变量和函数,JS引擎的处理原则是:

同名函数,后声明的函数会覆盖之前声明的函数,故JS编译阶段会选择最后声明的函数;

变量和函数同名,那么编译阶段,变量的声明会被忽略。这是由于函数提升的优先级比变量提升的优先级要高一些,且不会被变量声明覆盖。[1]

生成变量环境对象后,JS引擎会把声明以外的代码编译为字节码。

执行阶段

        在执行阶段会逐行执行可执行代码。

        综上我们了解了JS代码(引入的JS文件也是如此)都是先编译后执行,其实JS执行机制会出现变量提升的真正原因是JS不支持块级作用域。这门语言设计之初,没有块级作用域,将作用域内部的变量统一提升,直接导致函数中的变量无论何处声明,在编译阶段都会被提升到执行上下文的变量环境中,所以这些变量在整个函数体内部的任何地方都可以被访问[2]。变量提升导致一些JS代码的运行结果不符合逻辑的问题,该如何解决呢?

JS如何同时支持变量提升和块级作用域?

        ES6通过引入块级作用域(注:从 ES3 开始, try/catch 结构在 catch 分句中具有块作用域)关键字,解决变量提升导致的变量覆盖、变量污染等问题。需要注意,引入块级作用域后执行上下文会有些变化。执行函数时才会进行编译,抽象语法树(AST)在进入函数阶段就生成了,并且函数内部作用域是已经明确,所以进入块级作用域不会有编译过程。只不过通过let或者const声明的变量,会在执行到块级作用域的时被创建并追加到词法环境(LexicalEnvironment),但是在该变量没有赋值之前,引用该变量JavaScript引擎会抛出错误即暂时性死区。当这个块执行结束之后,追加到词法环境的内容又会销毁掉。

        例如下图例子中var声明的变量啊,a,c会在编译阶段存入变量环境,let声明的变量b会被存放到词法环境(编译时变量环境和词法环境最顶层数据已经确定);作用域块中的let变量b,d会在执行到作用域后被存放到词法环境单独区域(词法环境内部维护了一个类似调用栈的小型栈结构,最外层变量在栈底,进入的块级作用域在栈顶,执行完成则出栈),但暂不会被赋值。上述变量执行上下文中查找顺序如图中箭头

JS 执行机制_第2张图片

let/const声明的变量,是否有变量提升?

        实际上let/const声明的变量,都可以生成块级作用域,但仍然会提前被收集到变量对象中(此处描述更准确的是let和const会被收集到执行上下文的词法环境LexicalEnvironment中),但和var不同的是,let/const定义的变量,会被置为标志位,而不是给他赋值undefined。因为完全没有赋值,即使变量提升了,我们也不能在赋值之前调用它,这就是我们常说的暂时性死区[3]。

[1] 变量提升:JavaScript代码是按顺序执行的吗 07 | 变量提升:JavaScript代码是按顺序执行的吗?-极客时间

[2] 块级作用域:var缺陷以及为什么要引入let和const: 09 | 块级作用域:var缺陷以及为什么要引入let和const?-极客时间

[3] 前端基础进阶(三):变量对象详解 https://www.jianshu.com/p/330b1505e41d

你可能感兴趣的:(JS,探究,javascript,开发语言,ecmascript)