news 2026/5/6 21:55:55

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/5/1 7:02:25

WINBOND华邦 W25Q256JVFIQ/烧录 SOP16 NOR FLASH

2.特性 新一代SpiFlash存储器系列-W25Q256JV:256兆位/32兆字节标准SPI:CLK、ICS、DI、DO 双SPI:CLK、/CS、100、101, 四通道SPI:CLK、/CS、I/00、I/01、I/02、I/033字节或4字节寻址模式 软件与硬件复位(1) 最高性能串行闪存 133MHz标准/双通道/四通道SPI时钟 -266/532MHz等效双…

作者头像 李华
网站建设 2026/5/6 0:04:47

文档转换工具终极指南:从零基础到高效应用

文档转换工具终极指南:从零基础到高效应用 【免费下载链接】pandoc Universal markup converter 项目地址: https://gitcode.com/gh_mirrors/pa/pandoc 文档转换是每个内容创作者和办公人员都会面临的日常需求。无论你是学生需要将论文转换为不同格式&#x…

作者头像 李华
网站建设 2026/4/24 8:53:49

快速获取 Red Hat Enterprise Linux 7.0 镜像 ISO 的完整指南

快速获取 Red Hat Enterprise Linux 7.0 镜像 ISO 的完整指南 【免费下载链接】RedHatEnterpriseLinux7.0镜像ISO下载指南 本仓库提供 Red Hat Enterprise Linux 7.0 镜像 ISO 文件的下载链接,方便用户快速获取并安装该操作系统。该镜像文件存储在百度网盘中&#x…

作者头像 李华
网站建设 2026/5/2 9:47:55

Qwen3-4B-Thinking-2507-FP8:轻量化模型重塑AI推理技术格局

Qwen3-4B-Thinking-2507-FP8:轻量化模型重塑AI推理技术格局 【免费下载链接】Qwen3-4B-Thinking-2507-FP8 项目地址: https://ai.gitcode.com/hf_mirrors/Qwen/Qwen3-4B-Thinking-2507-FP8 在人工智能技术快速演进的当下,轻量化模型正成为推动产…

作者头像 李华
网站建设 2026/5/1 6:14:30

教你使用服务器打造“隐蔽背单词”学习环境:ToastFish 通知栏背词工具

很多人背单词失败,并不是因为不想学,而是因为现实太碎: 上班上课很忙,抽不出完整的 30 分钟 打开背词 APP 又容易分心(刷着刷着就跑偏了) 公开背单词有点尴尬,尤其在办公室或课堂 记忆需要重复,但你总是忘记“复习这件事” 真正有效的学习往往不是“拼命学一小时”…

作者头像 李华