JavaScript 的執(zhí)行過程涉及多個關(guān)鍵步驟,從代碼加載到最終渲染或事件處理,其核心機制圍繞 引擎解析、執(zhí)行上下文管理、事件循環(huán)調(diào)度 展開。JavaScript 程序首先由引擎解析,瀏覽器或 Node.js 加載腳本后,進(jìn)行詞法分析和語法分析,檢查語法錯誤。隨后創(chuàng)建全局執(zhí)行上下文,初始化變量環(huán)境和詞法環(huán)境同步代碼按順序執(zhí)行,異步任務(wù)則被推入任務(wù)隊列等待調(diào)度。
一、javascript的執(zhí)行步驟是什么?
代碼加載與解析
瀏覽器環(huán)境:HTML 解析時遇到 <script> 標(biāo)簽,會暫停 DOM 構(gòu)建,立即下載并解析 JS 文件。
Node.js 環(huán)境:直接讀取文件內(nèi)容,進(jìn)入解析階段。
解析過程:
詞法分析:將代碼拆分為 Token。
語法分析:生成 抽象語法樹(AST),檢查語法錯誤。
編譯:V8 等引擎將 AST 轉(zhuǎn)換為字節(jié)碼。
執(zhí)行上下文(Execution Context)創(chuàng)建
全局上下文:代碼首次運行時創(chuàng)建,包含全局變量、this 綁定。
函數(shù)上下文:函數(shù)調(diào)用時創(chuàng)建,包含參數(shù)、局部變量、this。
詞法環(huán)境(Lexical Environment):存儲變量聲明和函數(shù)定義,形成作用域鏈。
變量環(huán)境(Variable Environment):僅存儲 var 變量,存在變量提升。
變量提升與初始化
var 變量:在編譯階段被提升到作用域頂部,初始值為 undefined。
函數(shù)聲明:整體提升。
let/const 變量:存在“暫時性死區(qū)”(TDZ),在聲明前訪問會報錯(ReferenceError)。
示例:
javascriptconsole.log(a); // undefined(var 提升)console.log(b); // ReferenceError(let 未初始化)var a = 1;let b = 2;
代碼逐行執(zhí)行
同步代碼:按順序執(zhí)行,遇到表達(dá)式則計算值,遇到賦值則更新變量。
異步代碼:
宏任務(wù)(Macrotask):如 setTimeout、setInterval、I/O 操作,由事件隊列管理。
微任務(wù)(Microtask):如 Promise.then、MutationObserver,優(yōu)先級高于宏任務(wù)。
執(zhí)行順序:同步代碼 → 微任務(wù)隊列 → 宏任務(wù)隊列。
事件循環(huán)(Event Loop)調(diào)度
調(diào)用棧(Call Stack):存儲當(dāng)前執(zhí)行的函數(shù)調(diào)用鏈,棧頂函數(shù)執(zhí)行完畢后彈出。
任務(wù)隊列:
微任務(wù)隊列:當(dāng)前調(diào)用棧清空后立即執(zhí)行。
宏任務(wù)隊列:微任務(wù)執(zhí)行完畢后,從隊列中取出下一個任務(wù)執(zhí)行。
示例:
javascriptsetTimeout(() => console.log("Timeout"), 0);Promise.resolve().then(() => console.log("Promise"));console.log("Sync");// 輸出順序:Sync → Promise → Timeout
二、JavaScript 如何運行程序?
瀏覽器中的運行流程
HTML 解析:構(gòu)建 DOM 樹,遇到 <script> 時暫停解析,加載 JS 文件。
JS 執(zhí)行:解析代碼 → 創(chuàng)建全局上下文 → 執(zhí)行同步代碼 → 渲染引擎等待 JS 完成。
異步優(yōu)化:
使用 defer 或 async 屬性加載腳本,避免阻塞 DOM 構(gòu)建。
通過 requestAnimationFrame 優(yōu)化動畫性能。
Node.js 中的運行流程
事件驅(qū)動架構(gòu):基于 libuv 庫處理 I/O 操作,通過事件循環(huán)調(diào)度非阻塞任務(wù)。
模塊系統(tǒng):使用 CommonJS或 ES Modules管理代碼依賴。
示例:
javascriptconst fs = require("fs");fs.readFile("file.txt", "utf8", (err, data) => {console.log(data); // 異步讀取文件});
關(guān)鍵機制總結(jié)
單線程與異步:JS 引擎本身是單線程的,但通過事件循環(huán)和任務(wù)隊列實現(xiàn)并發(fā)。
非阻塞 I/O:通過回調(diào)、Promise、Async/Await 避免耗時操作阻塞主線程。
作用域與閉包:函數(shù)執(zhí)行時捕獲當(dāng)前詞法環(huán)境,形成閉包。
三、示例:完整執(zhí)行流程
javascriptconsole.log("Start"); // 同步代碼 1setTimeout(() => console.log("Timeout"), 0); // 宏任務(wù)Promise.resolve().then(() => {console.log("Promise 1");setTimeout(() => console.log("Nested Timeout"), 0); // 嵌套宏任務(wù)});Promise.resolve().then(() => console.log("Promise 2")); // 微任務(wù)console.log("End"); // 同步代碼 2// 輸出順序:// Start → End → Promise 1 → Promise 2 → Timeout → Nested Timeout
四、總結(jié)
JavaScript 的執(zhí)行遵循 “解析 → 上下文創(chuàng)建 → 同步執(zhí)行 → 異步調(diào)度” 的流程,核心依賴 執(zhí)行上下文、作用域鏈、事件循環(huán) 三大機制。理解這些步驟能幫助你:
調(diào)試變量作用域問題。
優(yōu)化異步代碼性能。
合理設(shè)計模塊化架構(gòu)。
引擎通過調(diào)用棧管理函數(shù)執(zhí)行,同步代碼直接運行,遇到異步操作時,宏任務(wù)進(jìn)入宏任務(wù)隊列,微任務(wù)進(jìn)入微任務(wù)隊列。當(dāng)前調(diào)用棧清空后,先執(zhí)行所有微任務(wù),再從宏任務(wù)隊列中取出一個任務(wù)執(zhí)行,如此循環(huán)形成事件循環(huán)。這一機制保證了單線程下的非阻塞運行,同時通過作用域鏈和閉包管理變量生命周期。