JS运行原理

120 阅读6分钟

1. ECMA语法词汇概念解释

1. 执行上下文(Execution Context)

  • 定义:JavaScript代码执行的环境,每个函数调用都会创建一个新的执行上下文。
  • 组成
    • 变量对象(VO)/词法环境(Lexical Environment) :保存变量、函数声明和参数。
    • 作用域链(Scope Chain) :用于查找变量。
    • this 值:当前执行上下文中 this 的指向。
  • 类型
    • 全局执行上下文(只有一个)
    • 函数执行上下文(每次函数调用都会创建)

2. 执行上下文栈(Execution Context Stack / Call Stack)

  • 定义:JS引擎用来管理执行上下文的栈结构,也叫调用栈。
  • 作用:控制函数调用的顺序,先进后出(LIFO)。
  • 过程
    • 当一个函数被调用时,新的执行上下文被推入栈顶。
    • 函数执行完毕后,该上下文被弹出栈。

3. 作用域(Scope)

  • 定义:变量或函数可访问的范围。
  • 目的:控制变量的可见性和生命周期,防止命名冲突。
  • 类型
    1. 全局作用域(Global Scope)
      • 变量和函数在全局范围内声明,可以在代码的任何地方被访问。
      • 这些变量和函数在整个程序运行期间都存在。
    2. 函数作用域(Function Scope)
      • 使用var关键字声明的变量具有函数作用域,意味着它们只在声明它们的函数内部有效。
      • 即使在嵌套的块(如循环或条件语句)中声明,这些变量也只在包含它们的最内层函数内可见。
    3. 块级作用域(Block Scope)
      • 使用letconst关键字声明的变量具有块级作用域,这意味着它们的作用范围是从声明的地方开始,直到包含它们的最近的闭合块结束。
      • 块可以是任何用花括号{}包围的代码段,例如循环体、条件语句等。
    4. 模块作用域(Module Scope)
      • 随着ES6引入模块系统(importexport),每个JavaScript模块都有自己的顶级作用域。
      • 在模块的顶层声明的变量、函数、类等,默认情况下不会污染全局命名空间,除非通过export显式地暴露给其他模块使用。
      • 提供了一种方式来避免全局命名冲突,并有助于更好地组织和封装代码。
    5. 词法作用域(Lexical Scope)/静态作用域
      • 词法作用域指的是在编写代码时确定的变量和函数的作用域,并且这种作用域关系不会受到运行时的变化影响。
      • 子函数会继承父函数的作用域,形成一个链式结构,这个链条被称为“作用域链”。

4. 作用域链(Scope Chain)

  • 定义:由多个作用域组成的链条,用于变量查找。
  • 形成方式:函数定义时就已经确定(基于词法作用域),而不是运行时。
  • 查找规则
    • 从当前作用域开始查找变量。
    • 如果找不到,沿着作用域链向上查找,直到全局作用域为止。

5. GO (Global Object)

  • 含义:JS引擎在代码执行之前,会在堆内存中创建一个全局上下文全局对象。在浏览器环境中通常是window对象,在Node.js环境中是global对象。
  • 作用:提供了一个全局范围内的命名空间,所有全局变量都是全局对象的属性,该对象包含,Date Array String setTimeout等。

6. VO (Variable Object)

  • 含义:变量对象。是一个抽象的概念,用来描述在执行上下文中存储变量、函数声明等的地方。在全局执行上下文中,它对应全局对象(如浏览器中的window对象);在函数执行上下文中,它就是活动对象(AO)。
  • 作用:作为执行上下文的一部分,用于存储变量声明、函数声明等信息。

7. AO (Activation Object)

  • 含义:活动对象。在ES5及之前版本中,当一个函数被执行时,它的执行上下文会创建一个活动对象。这个对象存储了该函数的所有局部变量、命名参数、参数集合以及this值。
  • 作用:用于表示函数调用时的上下文环境。

8. LE (Lexical Environment)

  • 含义:词法环境。自ES6起引入的概念,用于替代传统的VO/AO机制。词法环境包含两部分:环境记录(存储变量/函数声明)和对外部环境的引用(形成作用域链)。
  • 作用:定义了变量和函数在何处被存储,并决定了它们的查找方式。

9. VE (Variable Environment)

  • 含义:变量环境。也是自ES6起引入的一个概念,它是词法环境的一部分,专门负责存储当前执行上下文中通过var声明的变量和其他特定类型的声明(如function)。
  • 作用:用于保存当前执行上下文中的变量声明。

10. ER (Environment Record)

  • 含义:环境记录。属于词法环境的一部分,用于实际存储变量、函数声明等信息。
  • 作用:具体实现词法环境的功能,保存着当前词法环境中的标识符(变量名、函数名等)到值的映射关系。

上述图解:

2. ES3,JS运行解析

示例1: 相关解析概念:GO / VO / AO / Scope / Scope list image.png

示例2: 相关解析概念:VE / LE / ER / Scope / Scope list

执行前: image.png 执行后: image.png

3. ES6,JS运行解析

ES5/ES6之后的ECMAscript解析示意图:【重要】 image.png

4. 相关知识

4.1 闭包(Closure)

是指一个函数能够访问并记住其词法作用域(lexical scope),即使该函数在其作用域外执行。 内部函数引用了外部函数的变量,并且被外部保留下来,就形成了闭包

闭包的主要用途 image.png

闭包优点

image.png

闭包缺点 image.png

4.2 内存泄漏(Memory Leak)

内存泄漏是指程序在运行过程中分配了内存空间,但在不再需要时没有正确释放,导致这部分内存无法被重新利用。随着时间的推移,这可能导致系统性能下降甚至崩溃。

4.3 如何避免闭包导致的内存泄漏?

  1. 及时解除引用 手动将不再需要的闭包设为 null
  2. 使用弱引用结构(如 WeakMap / WeakSet) 适用于缓存或映射对象时,不阻止垃圾回收。
  3. 避免不必要的全局闭包 尽量减少将闭包暴露到全局作用域。
  4. 使用工具检测内存泄漏 如 Chrome DevTools 中的 Memory 面板,可以分析内存快照,查找未释放的对象。

4.4 变量提升(Hoisting)

JavaScript引擎在编译阶段,变量和函数声明会被移到其所在作用域的顶部。

变量提升优点

  • 灵活性:允许开发者在声明之前使用变量或函数,增加了编写代码的灵活性。
  • 初始化函数:有助于定义可以在其他函数或代码块之前调用的初始化函数,方便模块化的代码设计。

变量提升缺点

  • 混淆代码:可能导致代码阅读困难,因为变量和函数可以在它们声明之前被访问,容易让人误解代码的逻辑流程。
  • 意外覆盖:由于var声明的变量具有函数作用域而不是块作用域,可能会导致意外的变量覆盖或不正确的值,尤其是在循环或条件语句中。
  • 难以调试:错误可能不容易被发现,特别是当变量在作用域的不同部分被重新声明或赋值时。