news 2026/4/18 2:04:26

js 解析 和作用域的锁定 词法分析 和 语法分析

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
js 解析 和作用域的锁定 词法分析 和 语法分析

这部分内容,学了当然最好,没学,也不影响前端开发。当然,能了解肯定是比不了解的强。

依旧是无图无码,网文风格。我觉得,能用文字把逻辑或者概念表述清楚,一是对作者本身的能力提升有好处,二是对读者来说 思考文字表达的内容 有助于多使用抽象思维和逻辑思维能力,构建自己的思考模式,用现在流行的说法 就是心智模型。你自己什么都可以脑补,那不是厉害大了嘛。

上面的话不要相信,其实我就是为自己懒找的借口。

有些细节做了省略 有些边界情况做了简化表述。但是总体来说 准确性还是相当不错的。 如果有错漏的地方,还请多多指正。这是第一部分 词法和语法分析。

一.词法分析和语法分析

当浏览器从网络下载了js文件,比如app.js,浏览器引擎拿到的最初形态是一串**字节流 **。

  1. 识别:V8 首先要处理编码,V8 接收的是 UTF-8 编码的字节流,内部会转换为 UTF-16 处理字符串。

  2. 流式快速处理:引擎并不是等整个文件下载完才开始干活的。只要网络传过来一段数据,V8 的扫描器就开始工作了。 这样可以加快启动速度。此时的状态就是毫无意义的字符c,o,n,s,t, ,a, ,=, ,1,;

  3. 然后的这一步叫Tokenization 词语切分。 负责这一步的组件就是上面提到的叫Scanner(扫描器)。它的工作就像是一个切菜工,把滔滔不绝连绵不断的字符串切成一个个有语法意义的最小单位,叫做Token(记号)。看到这个词 ,大家是不是惊觉心一缩,没错,就是它,它们就是以它为单位来收咱钱的。

    scanner 内部是一个状态机。它逐个读取字符:

    • 读到c可能是const,也可能是变量名,继续。
    • 读到o,n,s,t凑齐了5个娃,且下一个字符不是字母(比如是空格),确认这是一个关键字 const。”(防止误判constant这种变量名)
    • 读到 空格 忽略,跳过去。
    • 读到1这是一个数字。

    这样就由原来的字节流变成了Token 流。这是一种扁平的列表结构。

    • 源码:const a = 1;
    • Token 流:
      • CONST(关键字)
      • IDENTIFIER(值为 “a”)
      • ASSIGN(符号 “=”)
      • SMI(小整数 “1”)
      • SEMICOLON(符号 “;”)

    这一步,注释和多余的空格和换行符会被抛弃。

  4. 现在就是解析阶段了

    其实解析是一个总称,它分为 全量解析 和 预解析 两种形式。

    这就是v8的懒解析机制。看到这个懒字,也差不多能明白了吧。

    对于那些不是立即执行的函数(比如点击按钮才触发的回调),V8 会先用预解析快速扫一遍。

    检查基本的语法错误(比如有没有少写括号),确认这是一个函数。并不会生成复杂的 AST 结构,也不建立具体的变量绑定,只进行最基础的闭包引用检查。御姐喜的结果是这个函数在内存里只是一个很小的占位符,跳过内部细节。

    而只有那些立即执行函数或者顶层代码,才会进入真正的全量解析,进行完整的 AST 构建。

    那么,问题就来了,v8怎么判断到底是使用预解析还是使用全量解析呢?

    它的原则就是 懒惰为主 全量为辅

    就是v8默认你写的函数暂时不会执行,除非是已经显式的通过语法告诉它,这段这行代码 马上就要跑 你赶快全量解析。

    下面 我们稍微详细的说一下

    • 默认绝大多数函数都是预解析

      v8认为js在初始运行时,仅仅只有很少很少一部分代码 是需要马上使用的 其他觉得大部分 都是要么是回调 要么是其他的暂时用不到的,所以,凡是具名函数声明、嵌套函数,默认都是预解析。

      function clickHandler() { console.log("要不要解析我"); } // 引擎认为 这是一个函数声明 看起来还没人调勇它 // 先不浪费时间了,只检查一下括号匹配吧, // 把它标记为 'uncompiled',然后跳过。"
    • 那么 如何才能符合它进行全量解析的条件呢

      1. 顶层代码

        写在最外层 不在任何函数内 的代码,加载完必须立即执行。

        判断依据:只要不在function块里的代码,全是顶层代码,必须全量解析。

      2. 立即执行函数

        那么这里有个问题,就是V8 如何在还没运行代码时,就知道这个函数是立即调用执行函数呢?

        答案就是 看括号()

        当解析器扫描到一个函数关键字function时,它会看一眼这个 function 之前有没有左括号(

        • 没括号

          function foo() { ... } // 没看到左括号,那你先靠边吧, 对它预解析。
        • 有括号

          (function() { ... })(); // 扫描器扫到了这个左括号 // 欸,这有个左括号包着 function // 根据万年经验,这是个立即执行函数,马上就要执行。 // 直接上大菜,全量解析,生成 AST
        • 其他的立即执行的迹象:除了括号,!+-等一元运算符放在function前面,也会触发全量解析

          !function() { ... }(); // 全量解析
    • 如果有嵌套函数咋办呢

      嵌套函数默认是预解析,即使外部函数进行的是全量解析,它内部定义的子函数,默认依然是预解析。只有当子函数真的被调用时,V8 才会暂停执行,去把子函数的全量解析做完 把 AST 补齐

      //顶层代码全量解析 (function outer() { var a = 1; // 内部函数 inner: // 虽然 outer 正在执行,但 inner 还没被调用 // 引擎也不确定 inner 会不会被调用。 // 所以inner 默认预解析。 function inner() { var b = 2; } inner(); // 直到执行到这一行,引擎才会回头去对 inner 进行全量解析 })();
    • 那么 引擎根据自己的判断 进行全量解析或者预解析,会出错吗

      当然会,

      如果是本该预解析的 结果判断错了 进行了全量解析 浪费了时间和内存生成了 AST 和字节码,结果这代码根本没跑。

      如果是本该全量解析的又巨又大又重的函数 结果判断错了 进行了预解析,然后马上下一行代码就调用了,结果就是 白白预解析了一遍,浪费了时间,发现马上被调用,又马上回头全量解析一边 又花了时间,两次的花费。

  5. 在上面只是讲了解析阶段的预解析和全量解析的不同,现在我们讲解析阶段的过程

    V8 使用的是递归下降分析法。它根据js 的语法规则来匹配 Token。

    它的规则类似于:当我们遇到const,根据语法规则,后面必须跟一个变量名,然后是一个赋值号,然后是一个表达式。

    过程示例:

    看到const创建一个变量声明节点。

    看到a把它作为声明的标识符

    看到=知道后面是初始值

    看到1创建一个字面量节点,挂在=的右边。

    而在这个阶段的同时,作用域分析也在同步进行,因为在构建 AST 的过程中,解析器必须要搞清楚变量在哪里

    它会盘算 这个a是全局变量,还是函数内的局部变量?

    如果当前函数内部引用了外层的变量,解析器会在这个阶段打上标记:“要小心,这个变量被逮住了,将来可能需要上下文来分配”。

    这个作用域分析比较重要,我们用稍微大点的篇幅来讲讲。

    首先 强烈建议 不要再去用以前的 活动对象AO vo 等等的说法来思考问题。应该使用现在的词法作用域 环境记录 等等思考模型。

    词法作用域 (Lexical Scoping)”的定义:作用域是由代码书写的位置决定的,而不是由调用位置决定的。

    这说明,引擎在还没开始执行代码,仅仅通过“扫描”源代码生成 AST 的阶段,就已经把“谁能访问谁”、“谁被谁逮住”这笔账算得清清楚楚了。

    一旦AST被生成,那么至少意味着下面的情况

    作用域层级被确定

    AST 本身的树状结构,就是作用域层级的物理体现。

    变量引用关系被识别

    这是解析器最忙碌的工作之一,叫做变量解析

    这里要注意:这个“找”的过程是在编译阶段完成的逻辑推导。

    闭包的蓝图被预判

    这一步是 V8 性能优化的关键,也就是作用域分析。

下面就是解释器Ignition该登场了。我们第二部分再见。

本文首发于: 掘金社区

同步发表于: csdn

博客园

码字虽不易 知识脉络的梳理更是不易 ,但是知识的传播更重要,

欢迎转载,请保持全文完整。

谢绝片段摘录。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/18 1:19:53

5 折交叉验证:让医学影像分割模型“既见树木也见森林”

K 折交叉验证 K 折交叉验证(K-fold Cross-Validation)是一种被广泛运用于机器学习与医学图像分析领域的模型评估策略,其核心思想是把整个数据集“物尽其用”:在数据量有限的情况下,既能训练出足够复…

作者头像 李华
网站建设 2026/4/18 7:15:55

5天掌握跨平台开发:ArkUI-X iOS适配从入门到精通

5天掌握跨平台开发:ArkUI-X iOS适配从入门到精通 【免费下载链接】arkui_for_ios ArkUI-X adaptation to iOS | ArkUI-X支持iOS平台的适配层 项目地址: https://gitcode.com/arkui-x/arkui_for_ios 想要一套代码同时征服iOS和HarmonyOS两大平台?A…

作者头像 李华
网站建设 2026/4/18 5:33:42

智能制造的底层基建:iPaaS 如何统一 ERP、MES 与 WMS 的数据流

在制造业数字化不断深化的今天,ERP、MES、WMS 正经历前所未有的复杂演进:ERP 云化、MES 个性化、WMS 多场景化,系统数量越来越多、部署模式越来越分散,而数据与流程却越来越难打通。要让计划、执行、仓储真正协同,仅靠…

作者头像 李华
网站建设 2026/4/18 3:48:07

Innovus命令手册:数字芯片设计的完整参考指南

Innovus命令手册:数字芯片设计的完整参考指南 【免费下载链接】Innovus命令手册下载分享 Innovus命令手册下载 项目地址: https://gitcode.com/Open-source-documentation-tutorial/70617 引言 Innovus命令手册是数字芯片设计工程师不可或缺的参考资料&…

作者头像 李华
网站建设 2026/4/18 3:45:07

正点原子HP20智能加热台,重新定义精密加热新标杆!

一篇文章带你看懂加热台 在电子研发与精密维修的世界里,一款趁手的加热台堪称“效率神器”。当行业还在为功率不足、控温不准、操作繁琐等问题困扰时,正点原子携重磅新品—HP20智能加热台强势破局!融合PD3.1智能协议与1℃微米级控温黑科技&am…

作者头像 李华