关于javascript:从预编译到声明提升到作用域链再到闭包

36次阅读

共计 1264 个字符,预计需要花费 4 分钟才能阅读完成。

JavaScript 引擎在代码执行之前会先进行如下操作:

  1. 先进行 分词 / 词法剖析 将语句宰割成词法单元 token,在对以后的整个作用域剖析实现后,JS 引擎会将 token进行 解析 / 语法分析 翻译成 AST(形象语法树)
  2. 预编译(预处理)
  3. 边解释边执行(不是纯解释,还有 JIT 编译,这里不开展了)

预编译(预处理)

有人说 JavaScript 没有预编译,是属于 语法分析 的一部分,有人说有 词法, 语法和代码生成 就曾经属于编译了,只是不须要提前编译的,而是在执行前的几毫秒才编译。所以 JavaScript 到底有没有预编译或者这些步骤属不属于预编译(预处理)大佬们的意见也不同,咱们就不探讨了,暂且把语法分析生成 AST 后到执行前的一些处理过程称之为 “预编译” 把。

  1. 全局预编译

    1. 创立 Global Object 全局上下文对象
    2. 找变量申明,将变量名绑定为 GO 的一个属性,值为undefined
    3. 找函数申明,将函数名绑定为 GO 的一个属性,值为 函数体
    4. 执行全局代码
  2. 部分预编译

    1. 创立 Activation Object 函数上下文对象
    2. 找行参,将行参名绑定为 AO 的一个属性,值为 undefined
    3. 找变量申明,将变量名绑定为 AO 的一个属性,值为 undefined
    4. 实参值赋值给形参
    5. 找函数申明,将函数名绑定为 AO 的一个属性,值为 函数体
    6. 函数生成 [[scope]] 属性,值为一个数组,数组的第一项是本函数的 AO, 下一项为外层函数的 AO,始终往外层,直到最初一项为 GO
    7. 执行函数代码

申明晋升,作用域链

下面预编译的前半部分就是所谓的 申明晋升,我有文章独自讲了申明晋升就不赘述了,详情点击这里。

函数执行前会生成 `[[scope]]` 属性,值为一个数组,数组的第一项是本函数的 AO
下一项为外层函数的 AO,始终往外层,直到最初一项为 GO

AO是函数的作用域,而 [[scope]] 这样的链式数组则是函数的作用域链,当函数进行 值查问 RHS时会先在 [[scope]] 中的第一项中寻找,如果没有找到则查找第二层,以此类推直到查找到 GO 为止。

闭包

函数 FN执行完结后,这个 函数 FNAO 的生命周期就应该完结了,须要被销毁。然而有非凡状况,尽管说是非凡状况,然而开发中十分常见,就是 函数 FN执行完结后 return 了一个 外部函数 A 并被内部保留后,原本应该被销毁的 函数 FNAO 便无奈被销毁,因为函数 A 的 [[scope]] 保留着 函数 FNAO 作为本人原型链的一部分,只有保留 函数 A 的变量不被销毁或者变量所在的 AO 不被销毁则 函数 FNAO 永远不会被销毁,并且变量保留的 函数 A 能够始终拜访 函数 FN中的变量和值,这样的景象叫做 闭包,这样的闭包大量呈现会导致内存透露或者加载过慢。

var Variable_1 = 1;
function FN() {
    var Variable_2 = 2;
    function A() {
        var Variable_3 = 3
        console.log(Variable_2);
    }
    return A;
}
var Function_A = FN();
FnVariable(); // 承受 函数 A 的变量 Function_A 通过调用能够输入 FN 中的 Variable_2

正文完
 0