一 JavaScript代码是如何被执行的?
解析(Parsing):
- 词法分析(Lexing/Lexical Analysis):这是编译的第一阶段,JavaScript引擎会读取源代码文本,将其分解成一系列称为“词法单元”(tokens)的小片段。每个词法单元代表一个最小的有意义的符号,比如关键字、标识符、运算符、字符串字面量等。
- 语法分析(Parsing):接下来,词法单元被组织成一个抽象语法树(Abstract Syntax Tree, AST)。AST是一个树状结构,它表示了源代码的结构和语法,使得引擎能更容易地理解和操作代码的逻辑。
预编译/编译(Pre-compilation/Compilation):
- 对于函数和模块,JavaScript引擎会在它们实际被调用之前进行预编译,这包括变量和函数声明的提升(hoisting)等操作。全局变量和函数会被添加到全局执行上下文中,而函数内的变量和函数声明则在每次函数调用时创建新的执行上下文时处理。
- 部分现代JavaScript引擎还会进行即时编译(JIT, Just-In-Time Compilation),将JavaScript代码转换为更高效的机器码,以便在运行时执行。
创建执行上下文(Execution Context):
- 当JavaScript代码准备执行时,首先会创建一个全局执行上下文(对于不在函数内的代码)或者函数执行上下文(对于函数调用)。执行上下文包含了代码执行的环境信息,比如变量、函数定义、
this
的值等。
- 当JavaScript代码准备执行时,首先会创建一个全局执行上下文(对于不在函数内的代码)或者函数执行上下文(对于函数调用)。执行上下文包含了代码执行的环境信息,比如变量、函数定义、
执行(Execution):
- 执行栈(Call Stack):JavaScript使用执行栈来跟踪当前执行的函数调用序列。每当一个函数被调用,一个新的执行上下文会被压入栈顶,函数执行完毕后,其上下文会从栈顶弹出,控制权返回到上一层函数。
- 执行过程:在执行上下文中,变量被初始化,函数被定义,代码按顺序执行。如果遇到异步操作(如setTimeout、Promise等),它们会被安排到未来的某个时间点执行,而不会阻塞当前执行栈的执行。
- 微任务与宏任务处理:在当前执行栈的函数执行完毕后,会先执行所有微任务,然后再进行下一轮事件循环,处理宏任务。这一过程确保了异步操作的有序执行。
垃圾回收(Garbage Collection):
- 在代码执行过程中,垃圾回收机制会定期检查不再被使用的内存空间(不再有引用指向的对象),并将其释放回可用内存池,以减少内存泄漏,保持内存的有效利用。