- Published on
JavaScript 的原理
Functions (函数)
函数是 JavaScript 中可执行代码的基本单元。它们本身只是存储在内存中的对象,但在被调用 (invoked) 时,就成为了整个执行流程的触发器。每当一个函数被调用,它就会请求 JavaScript 引擎为它创建一个专属的运行环境,也就是一个新的执行上下文。
Execution Context (执行上下文)
执行上下文是 JavaScript 引擎执行代码时所需的完整环境。把它想象成一个独立的盒子,每个盒子都包含两样东西:
内存空间 (Memory / Variable Environment):这个部分用来存储当前上下文中声明的所有变量、函数和参数。在上下文的“创建阶段”,JavaScript 引擎会扫描代码,找到所有声明并在这个内存空间中为它们分配好位置(这就是所谓的“变量提升”)。
执行线程 (Thread of Execution):这个部分负责逐行读取、解析并执行代码。在上下文的“执行阶段”,执行线程会一行一行地运行你的逻辑。
因此,当代码执行时,实际上是执行线程在操作内存空间中的数据。
两种执行上下文
- 全局执行上下文:为整个脚本创建,包含全局变量和函数
- 函数执行上下文:为每个函数调用创建,包含该函数的局部变量和参数。
Call Stack (调用栈)
调用栈(或称执行栈)是一个具有后进先出 (LIFO) 特点的数据结构,专门用来管理和追踪所有执行上下文。为了更好地理解,我们来看一个具体的例子:
示例代码:
function functionB() {
// 断点 3: 进入 functionB
console.log('In B');
}
function functionA() {
// 断点 2: 进入 functionA
console.log('Calling B');
functionB();
console.log('Returned from B');
}
// 断点 1: 脚本开始
console.log('Script Start');
functionA();
console.log('Script End');
下面是上面代码执行时,调用栈的变化过程的可视化图解:
- 脚本开始执行,
functionA()
即将被调用。此时,只有全局执行上下文在调用栈中。
- 调用
functionA()
,functionA
的执行上下文被创建并压入栈顶。引擎现在开始执行functionA
内部的代码。
- 在
functionA
内部调用functionB()
,functionB
的执行上下文被创建并压入栈顶。引擎暂停执行functionA
,转而开始执行functionB
的代码。
functionB()
执行完毕并返回,functionB
的执行上下文从栈顶被弹出。控制权交还给functionA
,从它上次暂停的地方继续执行。
functionA()
执行完毕并返回,functionA
的执行上下文也被弹出。控制权交还给全局执行上下文。
最后,当所有全局代码执行完毕,全局执行上下文也从栈中弹出,调用栈清空,程序结束。