本篇我想來談談JavaScript從原始碼到成功執行的過程。在此之前先來看看直譯式語言
和編譯式語言
的概述(節錄自維基百科):
直譯語言(英語:Interpreted language)是一種程式語言類型。這種類型的程式語言,會將程式碼一句一句直接執行,不需要像編譯語言(Compiled language)一樣,經過編譯器先行編譯為機器碼,之後再執行。
編譯語言(英語:Compiled language)是一種程式語言類型,通過編譯器來實作,先將程式碼編譯為機器碼,再加以執行。
我們知道JavaScript是直譯式語言,理論上可能會認為程式碼會由上而下直接執行,若某行程式碼有錯,也會正常運作到該行程式碼才報錯。但事實真的如我們想像的這樣嗎?我們先來看看以下程式碼片段:
console.log('檢查點A');
let myPet cat;
console.log('檢查點B');
我們可以發現第二行很明顯有有錯的,但根據直譯語言由上而下直接執行的特性,應該會先輸出檢查點A然後才報錯。然而當我們查看控制台卻發現根本沒有任何輸出就直接報錯了!
JavaScript 從解析到執行的過程
為什麼會發生這種情況呢?這就要進入我們今天的主題了。
其實電腦還是無法直接看懂JavaScript程式碼的,仍是需要先透過轉譯器進行解析後才執行,其運作過程通常會經歷以下階段:
在JavaScript程式碼執行之前,首先需要將原始碼分解為稱為token
的小單元。這一過程稱為詞彙分析或單元化。token是程式碼的基本構建塊,如變數名稱、運算符、括號、數字、字符串等。這過程是將程式碼轉換為一個電腦容易理解的結構。
在解析階段,JavaScript的轉譯器會將這些token轉換成稱為AST
的抽象語法樹。AST是一個樹狀結構,其中每個節點表示程式碼的一個部分,例如函數聲明、變數賦值、運算式等等。AST的結構使得電腦能夠理解程式碼的語義並執行,例如對於程式碼let x = 10;
,AST將包含一個變數聲明節點,它有一個子節點表示賦值操作。
一旦AST構建完成,轉譯器可以遍歷AST並執行其中的程式碼。執行過程涉及變數賦值、運算、函數呼叫、條件判斷等操作,並在適當的時候產生結果或擲出錯誤等等。
我們來看看實際的例子,這裡會用到Esprima線上工具來做說明。
來看看以下程式碼:
let x = 10;
首先針對上述程式碼,轉譯器會先將其拆解成token如下:
[
{
"type": "Keyword",
"value": "let"
},
{
"type": "Identifier",
"value": "x"
},
{
"type": "Punctuator",
"value": "="
},
{
"type": "Numeric",
"value": "10"
},
{
"type": "Punctuator",
"value": ";"
}
]
完成第一階段後,接著會將AST解析成類似以下的樹狀結構:
{
"type": "Program",
"body": [
{
"type": "VariableDeclaration",
"declarations": [
{
"type": "VariableDeclarator",
"id": {
"type": "Identifier",
"name": "x"
},
"init": {
"type": "Literal",
"value": 10,
"raw": "10"
}
}
],
"kind": "let"
}
],
"sourceType": "script"
}
最後當以上步驟都完成,就會開始執行我們的程式碼啦。
回到我們一開始給的範例:
console.log('檢查點A');
let myPet cat;
console.log('檢查點B');
大致了解運作過程,我們就不難知道為什麼沒有輸出「檢查點A」了。因為這段程式碼在解析過程中就遇到問題(無法解析"cat"),也就不會有後面的執行階段,自然部會有任何輸出了。
這時可能有人會懷疑JavaScript真的是直譯式語言嗎?
我們再來看另一個例子:
console.log('檢查點A');
let myPet = cat;
console.log('檢查點B');
此時卻會發現跟剛剛的情況不一樣,再報錯之前有輸出"檢查點A"了!
這是因為第二個範例順利通過解析並順利執行了,執行過程中第一行可以順利輸出沒問題,但第二行因為"cat"未定義所以報錯,並不再執行剩下的程式碼。也就是說JS還是有保留直譯式語言的特性喔。
備註: 仍有其他因素會導致程式碼不會依上下行逐行執行的情況,會在之後提到。
今天介紹了JavaScript從解析到執行的過程,會經歷以下階段:
另外也簡單舉例了解析階段錯誤和執行階段錯誤的差別。
雖然在正式開發上比較不會直接碰到底層運作邏輯,但畢竟這是程式運作的基礎,所以我還是拿出來談一下。
最後跟各位說聲明天見~