你用 JavaScript、Python 或 C 寫了一段程式,按下執行,結果就跑出來了。
但電腦的 CPU 其實只認得一種語言:由 0 和 1 組成的二進位指令。
你寫的 if、for、function,CPU 完全看不懂。
那中間一定有人幫你「翻譯」了——這個翻譯的過程,就是這篇文章要講的重點。
你可能聽過「compile(編譯)」和「transpile(轉譯)」這兩個詞。
它們聽起來很像,但背後的目的完全不同。
這篇文章會用最簡單的方式,帶你一次搞懂。
Compile(編譯):讓機器看懂你的程式碼
Compile 的意思是把高階語言(high-level language)轉換成機器層級的程式碼(machine code)。
高階語言就是人類比較好讀寫的程式語言,像是 C、Java、Go 這些。
機器層級的程式碼則是 CPU 能直接執行的二進位指令,也就是一堆 0 和 1。
舉個例子,你寫了一行 C 語言:
int a = 10;編譯器(compiler)會把這行程式碼翻譯成二進位指令,看起來像這樣:
10111000 00001010 00000000 00000000 00000000對人類來說完全看不懂,但 CPU 讀到這串指令就知道:「把 10 這個數字存起來」。
翻譯完之後,CPU 就能直接執行了。
常見的編譯式語言包括 C、C++、Java、Go 和 Rust。
Java 編譯流程:從原始碼到 bytecode 再到機器碼
像 C 語言這樣直接編譯成機器碼,有一個麻煩:不同的作業系統和 CPU 架構,需要的機器碼格式不一樣。
Windows、macOS、Linux 各自要不同的機器碼。
也就是說,同一份程式碼你得針對每個平台各編譯一次,才能在不同電腦上執行。
Java 的做法不一樣,它把編譯分成兩段來解決這個問題。
第一段:程式碼 → bytecode
Java 編譯器不會直接產出機器碼,而是先轉換成一種叫做 bytecode(位元組碼)的中間格式。
舉個例子,你寫了這段 Java:
int a = 10;編譯器會把它轉換成 bytecode,看起來像這樣:
bipush 10
istore_1這段 bytecode 的意思是:「把數字 10 推入堆疊,然後存到變數 1 的位置」。
注意,bytecode 還不是真正的機器碼,CPU 沒辦法直接執行它。
第二段:bytecode → 機器碼
JVM(Java Virtual Machine,Java 虛擬機)負責在程式執行的時候,把 bytecode 即時轉換成當前電腦 CPU 能理解的機器碼。
Windows 上的 JVM 翻成 Windows 的機器碼,macOS 上的 JVM 翻成 macOS 的機器碼。
你只要編譯一次,產出通用的 bytecode,到哪台電腦上都能跑。
這就是 Java 「Write Once, Run Anywhere(寫一次,到處跑)」的核心概念。
這種「執行時才把中間碼翻譯成機器碼」的做法,有個專有名詞叫做 JIT(Just-In-Time Compilation,即時編譯),除了 Java 之外,C# 的 .NET 平台也是用類似的方式運作。
雖然多了一步,但最終目標還是一樣:讓機器能理解並執行你的程式碼。
Transpile(轉譯):讓另一群人或另一個平台看懂你的程式碼
JavaScript 這個語言一直在進化,每隔幾年就會推出新的語法,讓程式碼變得更簡潔好讀。
但問題是,不是所有瀏覽器都有跟上。
你的使用者可能還在用舊版的 Safari、IE,或是某些嵌入式裝置的瀏覽器。
這些瀏覽器看到新語法,不會自動翻譯,而是直接報錯、整頁掛掉。
那怎麼辦?難道為了相容性,就放棄新語法,回去寫又臭又長的舊寫法嗎?
不用。Transpile(轉譯)就是來解決這個問題的。
Transpile 的意思是把程式碼從一種高階語言轉換成另一種高階語言。
轉換完的結果,人類還是看得懂,不會變成 0 和 1。
舉個例子,你用現代 JavaScript 寫了這段程式碼:
const add = (a, b) => a + b;這是 ES6 的箭頭函式語法,簡潔好讀。
但有些舊版瀏覽器看不懂這種寫法。
這時候,轉譯器(transpiler)就會幫你把它轉換成舊版 JavaScript:
var add = function(a, b) {
return a + b;
};邏輯完全沒變,只是語法變成舊版瀏覽器能理解的寫法。
你放心寫最新、最乾淨的程式碼,轉譯器負責幫你搞定相容性。
寫一次程式碼,到處都能用。
常見的 Transpile 範例
以下是幾個你在實務中常遇到的轉譯場景。
TypeScript → JavaScript
TypeScript 是 JavaScript 的「加強版」,最大的特色是加了型別系統。
你可以在寫程式的時候就標明每個變數是什麼型別,讓錯誤在寫的階段就被抓到,而不是等到程式跑起來才爆掉。
function add(a: number, b: number): number {
return a + b;
}但瀏覽器只認標準的 JavaScript,看不懂 : number 這種型別標記。
所以 TypeScript 編譯器會幫你把型別標記拿掉,轉譯成純 JavaScript:
function add(a, b) {
return a + b;
}邏輯完全沒變,只是那些「給開發者看的型別資訊」被移除了。
現代 JavaScript → 舊版 JavaScript(Babel)
前面已經看過箭頭函式的例子,但新語法不只箭頭函式。
像是解構賦值、模板字串、async/await 等等,都是舊版瀏覽器不認得的語法。
Babel 這個工具可以自動幫你把這些新語法轉成等價的舊寫法。
例如模板字串:
const msg = `Hello, ${name}!`;Babel 會轉成:
var msg = "Hello, " + name + "!";你寫新語法,Babel 負責翻譯,舊瀏覽器照樣能跑。
JSX → JavaScript(React)
如果你用過 React,一定寫過 JSX。
JSX 讓你可以在 JavaScript 裡面直接寫類似 HTML 的語法:
const element = <h1>Hello, world!</h1>;但 JSX 不是標準的 JavaScript,瀏覽器完全看不懂。
React 的轉譯工具會把它轉換成標準的函式呼叫:
const element = React.createElement("h1", null, "Hello, world!");看起來差很多,但執行的結果是一樣的。
JSX 只是一個讓開發者「寫起來更直覺」的語法糖,最終都會被轉譯成純 JavaScript。
SASS → CSS
CSS 本身沒有變數、巢狀結構這些功能,寫大型專案的時候容易變得又長又重複。
SASS 就是為了解決這個問題,它讓你可以用變數和巢狀語法來寫樣式:
$primary: #3498db;
.button {
color: $primary;
&:hover {
color: darken($primary, 10%);
}
}但瀏覽器只認標準 CSS,所以 SASS 會被轉譯成:
.button {
color: #3498db;
}
.button:hover {
color: #217dbb;
}變數被替換成實際的值,巢狀結構被展開成瀏覽器看得懂的格式。
共同點
注意到了嗎?不管是 TypeScript、Babel、JSX 還是 SASS,轉譯後的結果全部都還是人類看得懂的程式碼。
這就是 Transpile 和 Compile 最大的差異:Compile 的產出是給機器看的 0 和 1,Transpile 的產出還是給人看的程式碼。
Compile vs Transpile 快速對照
Compile 和 Transpile 重點整理
記住這個核心觀念就夠了:
- Compile:程式碼 → 給機器看的東西(0 和 1)。
- Transpile:程式碼 → 給人或其他平台看的程式碼。
目標不同,工具不同,不要再搞混了。