资讯专栏INFORMATION COLUMN

js-编译原理与声明提升

zhongmeizhi / 2109人阅读

摘要:编译原理在传统编译语言的流程中,程序中的一段源代码在执行之前会经历三个步骤,统称为编译。定义声明是在编译阶段进行的,而赋值是在执行阶段进行的。

编译原理

在传统编译语言的流程中,程序中的一段源代码在执行之前会经历三个步骤,统称为“编译”。

词法分析
将由字符组成的字符串分解成(对编程语言来说)有意义的代码块,这些代码块被称为词法单元(token)。

语法分析
这个过程是将词法单元流(数组)转换成一个代表了程序语法结构的树。这个树被称为“抽象语法树”(AST)。

代码生成
将 AST 转换为可执行代码的过程被称为代码生成。AST 转化为一组机器指令。

JavaScript 引擎要复杂得多。例如,在语法分析和代码生成阶段有特定的步骤来对运行性能进行优化,包括对冗余元素进行优化等。
与其他语言不同,JavaScript 的编译过程不是发生在构建之前的。对于 JavaScript 来说,大部分情况下编译发生在代码执行前的几微秒(甚至更短)的时间内。

引擎、编译器、作用域

引擎:
从头到尾负责整个 JavaScript 程序的编译及执行过程。

编译器:
负责语法分析及代码生成等脏活累活。

作用域:
负责收集并维护由所有声明的标识符(变量)组成的一系列查询,并实施一套非常严格的规则,确定当前执行的代码对这些标识符的访问权限。

编译器可以控制作用域、而引擎更多的是查询(有可能会抛出异常)。

引擎在查询作用域的时候,有两种查询方法:
LHS:当变量出现在赋值操作的左侧时,试图找到变量的位置。如果在顶层(全局作用域)中也无法找到目标变量,全局作用域中就会创建一个具有该名称的变量。
RHS:当变量出现在赋值操作的右侧时,简单地查询值。在所有嵌套的作用域中遍寻不到所需的变量,引擎就会抛出 ReferenceError 异常。

词法作用域

作用域有两种主要的工作模型:词法作用域(静态)、动态作用域;
词法作用域是在编译器词法分析的时候生成的。

eval和with会动态改变词法作用域,这样会影响js引擎编译阶段的优化工作,会慢。所以严格模式禁用

声明提升

js的代码在生成前,会先对代码进行预编译,编译的一部分工作就是找到所有的声明,然后建立作用域将其关联起来,因此,在当前作用域内包括变量和函数在内的所有声明都会在任何代码被执行前首先被处理。
定义声明是在编译阶段进行的,而赋值是在执行阶段进行的。

** var 与 let区别
编译过程中用var定义的变量,会默认分配undefined值;
用let定义的变量,不会有任何值分配,所以在let之前使用这个变量会报错。

提升的优先级:
函数提升的优先级比变量高(函数声明会提升到变量声明之前,变量声明一定会被忽略)
对于同名的变量声明,Javascript采用的是忽略原则,后声明的会被忽略
对于同名的函数声明,Javascript采用的是覆盖原则,先声明的会被覆盖

示例1:

console.log(c)        // ReferenceError
b = function c() {};

等号右边的是不会变量提升的哟,可以见得编译过程中没有管等号右边。

示例2:

b = function c() {};
console.log(c)        // ReferenceError

执行过程中创建了c变量,但是只能在函数c中使用

示例3:

b = function c() {
    c = 3;
};
b();
console.log(c);        // ReferenceError

等价于

b = function() {
   let c = 3;
   c = 4    // 没有赋值到window上哟
};
b();

console.log(c);        // ReferenceError

示例4

(function () {
    try {
        throw new Error();
    } catch (x) {
        var x = 1, y = 2;   // 注意这里的x被覆盖
        console.log(x);
    }
    console.log(x);
    console.log(y);
})();
// 1 undefiend 2

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/105771.html

相关文章

  • JavaScript 是如何工作的:解析、抽象语法树(AST)+ 提升编译速度5个技巧

    摘要:无论你使用的是解释型语言还是编译型语言,都有一个共同的部分将源代码作为纯文本解析为抽象语法树的数据结构。和抽象语法树相对的是具体语法树,通常称作分析树。这是引入字节码缓存的原因。 这是专门探索 JavaScript 及其所构建的组件的系列文章的第 14 篇。 想阅读更多优质文章请猛戳GitHub博客,一年百来篇优质文章等着你! 如果你错过了前面的章节,可以在这里找到它们: JavaS...

    raoyi 评论0 收藏0
  • Js基础知识(三) - 作用域闭包

    摘要:是词法作用域工作模式。使用可以将变量绑定在所在的任意作用域中通常是内部,也就是说为其声明的变量隐式的劫持了所在的块级作用域。 作用域与闭包 如何用js创建10个button标签,点击每个按钮时打印按钮对应的序号? 看到上述问题,如果你能看出来这个问题实质上是考对作用域的理解,那么恭喜你,这篇文章你可以不用看了,说明你对作用域已经理解的很透彻了,但是如果你看不出来这是一道考作用域的题目,...

    lemanli 评论0 收藏0
  • Js基础知识(三) - 作用域闭包

    摘要:是词法作用域工作模式。使用可以将变量绑定在所在的任意作用域中通常是内部,也就是说为其声明的变量隐式的劫持了所在的块级作用域。 作用域与闭包 如何用js创建10个button标签,点击每个按钮时打印按钮对应的序号? 看到上述问题,如果你能看出来这个问题实质上是考对作用域的理解,那么恭喜你,这篇文章你可以不用看了,说明你对作用域已经理解的很透彻了,但是如果你看不出来这是一道考作用域的题目,...

    XFLY 评论0 收藏0
  • Js基础知识(三) - 作用域闭包

    摘要:是词法作用域工作模式。使用可以将变量绑定在所在的任意作用域中通常是内部,也就是说为其声明的变量隐式的劫持了所在的块级作用域。 作用域与闭包 如何用js创建10个button标签,点击每个按钮时打印按钮对应的序号? 看到上述问题,如果你能看出来这个问题实质上是考对作用域的理解,那么恭喜你,这篇文章你可以不用看了,说明你对作用域已经理解的很透彻了,但是如果你看不出来这是一道考作用域的题目,...

    tanglijun 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<