JavaScript 的编译与执行原理

news2025/5/18 15:45:12

文章目录

  • 前言
    • 🧠 一、JavaScript 编译与执行过程
      • 1. 编译阶段(发生在代码执行前)
        • ✅ 1.1 词法分析(Lexical Analysis)
        • ✅ 1.2 语法分析(Parsing)
        • ✅ 1.3 语义分析与生成执行上下文
    • 🧰 二、执行阶段
      • ✅ 2.1 创建执行上下文(Execution Context)
      • ✅ 2.2 变量环境 vs 词法环境
    • 🔁 三、作用域、作用域链与闭包
      • ✅ 3.1 作用域(Scope)
      • ✅ 3.2 作用域链(Scope Chain)
      • ✅ 3.3 闭包(Closure)
    • 🔄 四、循环作用域与经典陷阱
      • ✅ 4.1 for 循环中 var 的作用域陷阱
    • 🔍 五、执行上下文栈(ECS)
    • 🔐 六、总结:如何理解闭包与作用域链的结合
    • 📌 图示记忆(简化示意)
    • 总结


前言

系统深入地讲解 JavaScript 的编译与执行原理,并结合作用域、闭包、作用域链等核心概念,构建一个完整的知识体系。


🧠 一、JavaScript 编译与执行过程

虽然 JavaScript 是解释型语言,但它在执行前仍然会经历编译阶段(由现代 JavaScript 引擎完成,如 V8)。整体过程如下:

1. 编译阶段(发生在代码执行前)

包括以下几步:

✅ 1.1 词法分析(Lexical Analysis)
  • 将源代码拆解成词法单元(Token)
  • 例如:let a = 10;leta=10;
  • 负责这一阶段的是词法分析器(Lexer)。
// 代码
let x = 10 + y;

// 生成的tokens
[
  { type: 'Keyword', value: 'let' },
  { type: 'Identifier', value: 'x' },
  { type: 'Punctuator', value: '=' },
  { type: 'Numeric', value: '10' },
  { type: 'Punctuator', value: '+' },
  { type: 'Identifier', value: 'y' },
  { type: 'Punctuator', value: ';' }
]

关联知识:
词法环境是 JavaScript 引擎内部用来管理变量和函数作用域的机制,它是理解作用域和闭包的核心概念。

词法环境的组成
一个词法环境包含两部分:

​​环境记录(Environment Record)​​:存储变量和函数声明的实际位置
​​对外部词法环境的引用(Outer Lexical Environment)​​:形成作用域链

// 示例代码
let x = 10;

function foo() {
  let y = 20;
  console.log(x + y);
}

// 对应的词法环境结构
globalLexicalEnvironment = {
  environmentRecord: { x: 10, foo: <function> },
  outer: null
}

fooLexicalEnvironment = {
  environmentRecord: { y: 20 },
  outer: globalLexicalEnvironment
}

词法环境的特性
​​静态性​​:在代码编写阶段就已确定(词法作用域)
​​嵌套性​​:可以形成多层嵌套结构
​​持久性​​:被闭包引用的词法环境不会被销毁

✅ 1.2 语法分析(Parsing)
  • 将 Token 转换为 抽象语法树(AST)
  • AST 是代码结构的树状表示,每个节点代表代码结构的一个成分。
  • 语法分析器(Parser)处理这一步。
let x = 10 + y;
// 生成的AST结构
{
  type: "VariableDeclaration",
  kind: "let",
  declarations: [
    {
      type: "VariableDeclarator",
      id: { type: "Identifier", name: "x" },
      init: {
        type: "BinaryExpression",
        operator: "+",
        left: { type: "Literal", value: 10 },
        right: { type: "Identifier", name: "y" }
      }
    }
  ]
}
✅ 1.3 语义分析与生成执行上下文
  • 变量提升作用域环境创建函数声明处理

  • 此时创建:

    • 执行上下文栈(Execution Context Stack)
    • 词法环境(Lexical Environment)
    • 变量环境(Variable Environment)

🧰 二、执行阶段

编译完成后,进入代码执行:

✅ 2.1 创建执行上下文(Execution Context)

每个函数/全局代码执行时,会创建一个上下文环境:

  • 包含:

    • 变量环境(变量/函数声明)
    • 词法环境(作用域)
    • this 绑定
    • 外部环境引用(outer)

✅ 2.2 变量环境 vs 词法环境

  • 变量环境

    • 存储变量、函数声明(var/函数声明)
  • 词法环境(Lexical Environment):

    • 变量环境 + 外部环境引用(outer)
    • 用于作用域链的构建。

🔁 三、作用域、作用域链与闭包

✅ 3.1 作用域(Scope)

  • 定义变量和函数的可访问范围

  • 分为:

    • 全局作用域
    • 函数作用域
    • 块级作用域(let/const)

✅ 3.2 作用域链(Scope Chain)

  • 当前执行上下文的词法环境中包含对上级词法环境的引用
  • 在查找变量时,从当前作用域出发,逐层向上查找,直到全局作用域。
function outer() {
  let a = 10;
  function inner() {
    console.log(a); // 通过作用域链访问 outer 的 a
  }
  inner();
}
outer();

✅ 3.3 闭包(Closure)

  • 闭包是函数+定义它的词法环境的组合
  • 当一个函数“脱离”了它定义时的作用域,仍然“记住”当时的变量。
function makeCounter() {
  let count = 0;
  return function () {
    return ++count;
  }
}
const counter = makeCounter();
counter(); // 1
counter(); // 2
  • count 保存在 makeCounter 的词法环境中,被返回的函数形成闭包访问它。

🔄 四、循环作用域与经典陷阱

✅ 4.1 for 循环中 var 的作用域陷阱

for (var i = 0; i < 3; i++) {
  setTimeout(() => {
    console.log(i); // 输出三个 3
  }, 0);
}
  • 原因:var 没有块级作用域,i 绑定在同一个词法环境中。
  • 修复方法:使用 let(创建新的词法环境)
for (let i = 0; i < 3; i++) {
  setTimeout(() => {
    console.log(i); // 输出 0 1 2
  }, 0);
}

🔍 五、执行上下文栈(ECS)

每当函数调用时,会创建新的执行上下文并压栈

  1. 创建全局执行上下文 → 入栈
  2. 调用函数 → 创建函数执行上下文 → 入栈
  3. 函数执行完毕 → 执行上下文出栈

🔐 六、总结:如何理解闭包与作用域链的结合

  1. JavaScript 中函数定义时就“捕获”了其父级词法环境(静态作用域)。
  2. 执行时,通过作用域链查找变量,先本地再向上查找。
  3. 如果内部函数延迟执行(异步或返回),那么闭包可以“保持”对外部变量的访问。

📌 图示记忆(简化示意)

makeCounter() 创建执行上下文
└── 词法环境 {
      count = 0,
      outer = Global
    }

返回的匿名函数 闭包:
└── 词法环境 {
      outer = makeCounter.LE
    }

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

总结

  • ​​词法环境​​是 JavaScript 作用域管理的核心机制,具有静态性和嵌套性
  • 循环中使用 let 会为每次迭代创建新的词法环境副本
  • ​​词法分析​​和​​语法分析​​是编译的前期阶段,与运行时词法环境不同
  • JavaScript 引擎通过这种机制实现块级作用域和闭包功能
  • 理解这些概念有助于编写正确的作用域代码和调试复杂的作用域问题

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2378597.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

NHANES指标推荐:FMI

文章题目&#xff1a;Exploring the relationship between fat mass index and metabolic syndrome among cancer patients in the U.S: An NHANES analysis DOI&#xff1a;10.1038/s41598-025-90792-9 中文标题&#xff1a;探索美国癌症患者脂肪量指数与代谢综合征之间的关系…

【JDBC】JDBC常见错误处理方法及驱动的加载

MySQL8中数据库连接的四个参数有两个发生了变化 String driver "com.mysql.cj.jdbc.Driver"; String url "jdbc:mysql://127.0.0.1:3306/mydb?useSSLfalse&useUnicodetrue&characterEncodingutf8&serverTimezoneAsia/Shanghai"; 或者Strin…

车载以太网驱动智能化:域控架构设计与开发实践

title: 车载以太网驱动专用车智能化&#xff1a;域控架构设计与开发实践 date: 2023-12-01 categories: 新能源汽车 tags: [车载以太网, 电子电气架构, 域控架构, 专用车智能化, SOME/IP, AUTOSAR] 引言&#xff1a;专用车智能化转型的挑战与机遇 专用车作为城市建设与工业运输…

如何利用技术手段提升小学数学练习效率

在日常辅导孩子数学作业的过程中&#xff0c;我发现了一款比较实用的练习题生成工具。这个工具的安装包仅1.8MB大小&#xff0c;但基本能满足小学阶段的数学练习需求。 主要功能特点&#xff1a; 参数化出题 可自由设置数字范围&#xff08;如10以内、100以内&#xff09; 支…

BGP路由策略 基础实验

要求: 1.使用Preva1策略&#xff0c;确保R4通过R2到达192.168.10.0/24 2.用AS_Path策略&#xff0c;确保R4通过R3到达192.168.11.0/24 3.配置MED策略&#xff0c;确保R4通过R3到达192.168.12.0/24 4.使用Local Preference策略&#xff0c;确保R1通过R2到达192.168.1.0/24 …

第9讲、深入理解Scaled Dot-Product Attention

Scaled Dot-Product Attention是Transformer架构的核心组件&#xff0c;也是现代深度学习中最重要的注意力机制之一。本文将从原理、实现和应用三个方面深入剖析这一机制。 1. 基本原理 Scaled Dot-Product Attention的本质是一种加权求和机制&#xff0c;通过计算查询(Query…

双向长短期记忆网络-BiLSTM

5月14日复盘 二、BiLSTM 1. 概述 双向长短期记忆网络&#xff08;Bi-directional Long Short-Term Memory&#xff0c;BiLSTM&#xff09;是一种扩展自长短期记忆网络&#xff08;LSTM&#xff09;的结构&#xff0c;旨在解决传统 LSTM 模型只能考虑到过去信息的问题。BiLST…

MySQL UPDATE 执行流程全解析

引言 当你在 MySQL 中执行一条 UPDATE 语句时&#xff0c;背后隐藏着一套精密的协作机制。从解析器到存储引擎&#xff0c;从锁管理到 WAL 日志&#xff0c;每个环节都直接影响数据一致性和性能。 本文将通过 Mermaid 流程图 和 时序图&#xff0c;完整还原 UPDATE 语句的执行…

亚马逊云科技:开启数字化转型的无限可能

在数字技术蓬勃发展的今天&#xff0c;云计算早已突破单纯技术工具的范畴&#xff0c;成为驱动企业创新、引领行业变革的核心力量。亚马逊云科技凭借前瞻性的战略布局与持续的技术深耕&#xff0c;在全球云计算领域树立起行业标杆&#xff0c;为企业和个人用户提供全方位、高品…

【实测有效】Edge浏览器打开部分pdf文件显示空白

问题现象 Edge浏览器打开部分pdf文件显示空白或显示异常。 ​​​​​​​ ​​​​​​​ ​​​​​​​ 问题原因 部分pdf文件与edge浏览器存在兼容性问题&#xff0c;打开显示异常。 解决办法 法1&#xff1a;修改edge配置 打开edge浏览器&#x…

RJ连接器的未来:它还会是网络连接的主流标准吗?

RJ连接器作为以太网接口的代表&#xff0c;自20世纪以来在计算机网络、通信设备、安防系统等领域中占据了核心地位。以RJ45为代表的RJ连接器&#xff0c;凭借其结构稳定、信号传输可靠、成本低廉等优势&#xff0c;在有线网络布线领域被广泛采用。然而&#xff0c;在无线网络不…

Redis持久化机制详解:保障数据安全的关键策略

在现代应用开发中&#xff0c;Redis作为高性能的内存数据库被广泛使用。然而&#xff0c;内存的易失性特性使得持久化成为Redis设计中的关键环节。本文将全面剖析Redis的持久化机制&#xff0c;包括RDB、AOF以及混合持久化模式&#xff0c;帮助开发者根据业务需求选择最适合的持…

DeepSeek 大模型部署全指南:常见问题、优化策略与实战解决方案

DeepSeek 作为当前最热门的开源大模型之一&#xff0c;其强大的语义理解和生成能力吸引了大量开发者和企业关注。然而在实际部署过程中&#xff0c;无论是本地运行还是云端服务&#xff0c;用户往往会遇到各种技术挑战。本文将全面剖析 DeepSeek 部署中的常见问题&#xff0c;提…

嵌入式培训之数据结构学习(五)栈与队列

一、栈 &#xff08;一&#xff09;栈的基本概念 1、栈的定义&#xff1a; 注&#xff1a;线性表中的栈在堆区&#xff08;因为是malloc来的&#xff09;&#xff1b;系统中的栈区存储局部变量、函数形参、函数返回值地址。 2、栈顶和栈底&#xff1a; 允许插入和删除的一端…

RabbitMQ--进阶篇

RabbitMQ 客户端整合Spring Boot 添加相关的依赖 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-amqp</artifactId> </dependency> 编写配置文件&#xff0c;配置RabbitMQ的服务信息 spri…

Android Studio报错Cannot parse result path string:

前言 最近在写个小Demo&#xff0c;参考郭霖的《第一行代码》&#xff0c;学习DrawerLayout和NavigationView&#xff0c;不知咋地&#xff0c;突然报错Cannot parse result path string:xxxxxxxxxxxxx 反正百度&#xff0c;问ai都找不到答案&#xff0c;报错信息是完全看不懂…

关于网站提交搜索引擎

发布于Eucalyptus-blog 一、前言 将网站提交给搜索引擎是为了让搜索引擎更早地了解、索引和显示您的网站内容。以下是一些提交网站给搜索引擎的理由&#xff1a; 提高可见性&#xff1a;通过将您的网站提交给搜索引擎&#xff0c;可以提高您的网站在搜索结果中出现的机会。当用…

基于QT(C++)OOP 实现(界面)酒店预订与管理系统

酒店预订与管理系统 1 系统功能设计 酒店预订是旅游出行的重要环节&#xff0c;而酒店预订与管理系统中的管理与信息透明是酒店预订业务的关键问题所在&#xff0c;能够方便地查询酒店信息进行付款退款以及用户之间的交流对于酒店预订行业提高服务质量具有重要的意义。 针对…

机械元件杂散光难以把控?OAS 软件案例深度解析

机械元件的杂散光分析 简介 在光学系统设计与工程实践中&#xff0c;机械元件的杂散光问题对系统性能有着不容忽视的影响。杂散光会降低光学系统的信噪比、图像对比度&#xff0c;甚至导致系统功能失效。因此&#xff0c;准确分析机械元件杂散光并采取有效抑制措施&#xff0c…

游戏引擎学习第289天:将视觉表现与实体类型解耦

回顾并为今天的工作设定基调 我们正在继续昨天对代码所做的改动。我们已经完成了“脑代码&#xff08;brain code&#xff09;”的概念&#xff0c;它本质上是一种为实体构建的自组织控制器结构。现在我们要做的是把旧的控制逻辑迁移到这个新的结构中&#xff0c;并进一步测试…