當我輸入這行程式碼,電腦會如何去運行這行程式碼呢?
1 | var ming = '小明'; |
其實,電腦、瀏覽器無法直接閱讀我們所撰寫的 JS
💡 電腦閱讀程式碼的邏輯,跟「我撰寫程式碼的邏輯」是不同的因此,在運行程式碼前,要先經過「編譯 or 直譯」
直譯式語言(Interpreted language)
🎃 JavaScript 是屬於「直譯式語言」–> 在執行之前,沒有經過編譯
📖 「直譯式語言」的運作邏輯:
- 將原始碼透過「直譯器」,直接「生成代碼」
- 生成代碼後,就可以直接運行了
💡 電腦無法直接看懂「我所撰寫的原始碼」,電腦需要透過「直譯器」來閱讀我所寫的原始碼
‼️ 「直譯式語言」–> 錯誤會直接反映在「執行環境」中
因為 JS 不會預先編譯(不會“預先除錯”),因此「JS 的錯誤都會直接呈現在 Console 上」
「直譯式語言」的優點
📍 優點:更有彈性、不需要「預先定義型別」
- 先將語法「基本單元化」:把 JS 語法中的「詞彙、標點符號」一一解析出來(轉為 token)
- 把「一個一個的 token」轉為「抽象結構樹」,將整個「原始碼的結構」定義出來 (此時,還沒有執行程式碼)
- 最後,再將代碼生成出來 (代碼生成出來後,才會運行程式碼)
步驟一:語法基本單元化(Tokenizing)
🔧 直譯網站 Esprima
📖 「token」是「標誌;表示;象徵」的意思(noun)
–> 「Tokenizing」就是:將一個一個字詞都轉成「token」
1 | var ming = '小明'; |
當我輸入這行程式碼,電腦不會直接了解到「我現在要宣告一個變數,叫做’小明’」,而是直接先將「各個字詞的意思」一一解析出來
那麼,電腦到底是如何解析這行程式碼呢?
就好像小時候學寫作文,只是先把結構取出來
📖 Esprima 這個工具可以呈現出原始碼在「基本單元化」之後的結構
把這行var ming = '小明';
貼到左側框框內後,在右側選擇「Tokens」頁籤,就可以看到,它把詞彙一一解析出來的結果:
1️⃣ 辨識出var
是一個「關鍵字」
1 | { |
2️⃣ 電腦不會知道ming
是一個「變數」,電腦只知道ming
是一個「被 user 定義出來的文字」
1 | { |
3️⃣ 電腦不會知道=
是用來「賦予值的」,電腦只知道=
是一個「標點符號」
1 | { |
4️⃣ 電腦不會知道'小明'
是代表什麼意思,電腦只知道'小明'
是一個「字串」
1 | { |
5️⃣ 電腦不會知道;
是用來「做為語句的結尾」,電腦只知道;
是一個「標點符號」
1 | { |
步驟二:轉為「抽象結構樹」
接下來,電腦會把「剛剛解析出來的 token」,轉為電腦看得懂的「抽象結構樹」
點擊「Tree」頁籤,就可以看到「抽象結構樹」,它類似於 JSON 的格式
‼️ 一直到「定義出抽象結構樹」這個步驟,電腦才知道「現在正在定義一個變數」,但是「還沒有執行」定義變數的動作(真正的執行是在“代碼生成”之後)
🎃 「VariableDeclarator」代表:var ming = '小明';
這行程式碼主要就是要「定義一個變數」
🎃 「name: ming」代表:user 自訂的變數字詞
🎃 「value: 小明」代表:把「小明」這個詞彙,賦予到變數上
🎃 最下方有個「kind: var」代表:定義變數所使用的方法是「var」
步驟三:代碼生成
🎃 會因為執行環境不同,生成的代碼都不一樣
執行環境有可能是:瀏覽器 或是 Node.js
如果將程式碼改成這樣寫:
1 | ming = '小明'; |
透過「抽象結構樹」,可以看到,這樣寫的話,會跟var ming = '小明';
是完全不一樣的意思
🎃 「AssignmentExpression」代表:是一個「assign 的表達式」–> 把「'小明'
這個值」直接賦予(指派)在「變數ming
」上