面對 JavaScript 核心觀念時,總是感覺似懂非懂,於是就這樣不求甚解地繼續學習程式。但該面對的最終還是得面對,有些東西總會像鬼一樣,沒解決就會陰魂不散的回來,表達式 (expression) 與陳述式 (statement) 這兩個好兄弟就是其中之一。
還好有 Josh Comeau 深入淺出的文章,把我從五里霧中拉了出來,應 該 吧。這篇文章將以此基礎,就我的理解加其他輔助資料撰寫而成,如有錯誤還煩請指教。那就廢話少說開始吧。

Photo Credit: Ferenc Almasi, Unsplash
什麼是表達式?
如果要說表達式是什麼?最簡單的回答就是「能夠產生值的程式碼」,它可以是:
- 任何的資料型別,像是
- 數字:
168
- 字串:
"string"
- 布林:
true
、false
- 陣列:
["arr", "ray", "yee"]
- 物件:
{ type: "object" }
- 數字:
- 運算子產生的結果,常見的有
- 賦值運算子:
=
→ 回傳「所賦予的值」 - 算數運算子:
+
、-
、*
、/
、%
→ 回傳「運算後的結果」 - 比較運算子:
>
、<
、==
、===
→ 回傳「布林值」 - 邏輯運算子:
||
、&&
、!
||
:回傳「布林值」(「或」的判斷),或「第一個真值」&&
:回傳「布林值」(「且」的判斷),或「第一個假值」!
:回傳「翻轉後的布林值結果」
- 條件(三元)運算子:hasValue
?
"expressions":
"statements"
→ 依據條件真假(hasValue 處),決定回傳值:
若為真,回傳「冒號之前的值」;若為假,回傳「 冒號之後的值」
- 賦值運算子:
- 執行函式後產生的結果,以下有幾種情況:
// 1. 有明確的回傳值
function execFnHasReturn() {
console.log("This function will return something after execution");
return "I am returned value";
}
console.log(execFnHasReturn()); // 執行後回傳 "I am returned value"
// 2. 有 return 但沒有回傳值
function execFnEmptyReturn() {
console.log("This function will return 'undefined' value after execution");
return;
}
console.log(execFnEmptyReturn()); // 執行後回傳 undefined
// 3. 沒有 return
function execFnNoReturn() {
console.log("This function will return 'undefined' value after execution");
}
console.log(execFnNoReturn()); // 執行後回傳 undefined
→ 無論回傳值是否為 undefined
,都不改執行函式是表達式的事實
- 調用方法的結果,方法本質上也是函式的一種,回傳值會依方法而定:
["apple","banana"].push("orange"); // 回傳 3,新增項目後陣列的長度
[1, 2, 3].forEach(i => console.log(i)); // 回傳 undefined
[3, 1, 2].sort() // 回傳 [1, 2, 3],修改後的陣列
"yee".toUpperCase() // 回傳 "YEE"
- 其他:變數、正規表達式、函式表達式……
看了這麼多表達式的型態,可以歸納幾個重點:
- 表達式可以透過運算子,形成更複雜的表達式,例如:
88 / (1.88 * 1.88)
→88 / (1.88 * 1.88)
、(1.88 * 1.88)
、88
、1.88
都是表達式hasValue ? "expressions" : "statements"
→hasValue
、"expressions"
、"statements"
都是表達式
- 會回傳值,即便該值是
undefined
,也是表達式
什麼是陳述式?
JavaScript 是由一系列陳述式組成,陳述式不會產生值,它的作用在於告訴瀏覽器「該執行什麼動作」以及「控制程式的流程走向」,主要分為以下幾種:
- 宣告:告訴瀏覽器要為宣告的內容分配記憶體空間
- 變數的宣告:用
var
、let
、const
關鍵字來宣告變數 - 函式的宣告:用
function
來宣告函式 - 類別的宣告:用
class
來宣告類別
- 變數的宣告:用
- 流程控制:告訴瀏覽器執行程式碼的流程該怎麼走
- 條件判斷:
if...else
、switch
、while
/do...while
- 迴圈執行:
for
、 for...in
、for...of
- 語句中斷、回傳或跳轉:
return
(throw
)、break
、continue
- 例外處理:
try...catch
(&finally
)
- 條件判斷:
- 模組相關:
import
、export
- 其他:
debugger
(中斷執行)、async
/await
(非同步處理)、{ }
(區塊)…
陳述式透過這些關鍵字建立出一個敘述的邏輯架構,但要有內容才是完整的陳述句,而這些內容,則需要靠表達式來提供,例如,
// if 陳述式
if (/* 表達式 */) {
//...
}
// 範例
if (age >= 18 && hasID) {
// ...
}
// 迴圈陳述式
for (/* 初始化;條件;更新 */) {
// ...
}
// -> 條件、更新須為表達式
// -> 初始化可用變數宣告或表達式,以 let 宣告可限定作用域
// 範例
for (let i = 0; i < array.length; i++) {
// ...
}
// switch 陳述式
switch (/* 表達式 */) {
case /* 表達式 */:
// ...
}
// 範例
switch (status) {
case "success":
// ...
}
同時,我們可以透過宣告變數,將表達式的值存入該變數之中:
let num = 168;
let str = "string";
let isBool = true;
let arr = ["arr", "ray", "yee"];
let obj = { type: "object" };
let text = "yee".toUpperCase()
學習 React 帶來的啟發
撰文當下,我對 React 的理解還不夠深入,不過在學習的過程中,似乎讓我對表達式與陳述式的差別更加清明了。
在 React 中,我們會透過一種叫做 JSX 的語法,在 JavaScript 中撰寫 HTML 的結構。JSX 能夠讓我們可以在 HTML 的結構中,透過大括號 {}
將 JavaScript 程式碼插入 HTML 裡。
不過,這個大括號並非毫無限制,我們並沒有辦法隨心所欲地放入任何的 JavaScript 程式碼,我們看看下方兩個 JSX 的例子,同樣是處理條件邏輯,但其中一個會出錯:
- 大括號內用
if...else
語句(陳述式):會出現語法錯誤
function DisplayExamResult({ examScore }){
return (
<div>
{if (examScore >= 60){
return "Congrats! You passed!"
} else {
return "What a pity, you failed!"}}
</div>
);
}
- 必須使用「三元運算子」(表達式)處理條件的邏輯:
function DisplayExamResult({ examScore }){
return (
<div>
{examScore >= 60 ? "Congrats! You passed!" : "What a pity, you failed!"}
</div>
);
}
一樣是條件邏輯,我們只能在大括號內放入表達式,為什麼呢?
JavaScript 作為一種……語言?
拿 Comeau 的比喻來形容(我也覺得這個比喻極讚!),JSX 的大括號 {}
就像是 HTML 模板中的插槽,我們需要在這個插槽中放入「表達式」,而不是「陳述式」。因為只有表達式才會產生具體的值,才能變成實際的東西被渲染到畫面上。
雖然可能不是很精確,不過用英文基礎的「主詞-動詞-受詞」(S-V-O) 句型來說,如果挖空主詞、受詞的位置,這個空格可以用很單純的名詞填補,你也可以套上形容詞、甚至形容詞子句讓這個名詞變得更加複雜,但最終你填上的東西,還是得屬於名詞類,因為在英文文法中,主詞、受詞只能是名詞類的東西。
### S V O 句型
The woman wrote a program.
### 將 S、 O 挖空
{ S } wrote { O }
### 用更複雜的 S、O 撰寫
The elegant **woman** you saw yesterday
wrote
a complicated **program** by herself to change people's lives.
表達式的撰寫方式可能有上百種,但我覺得它所傳出來的「值」就有點像是這種概念。回到 JSX 的大括號裡為什麼不能放 if...else
語句的問題,用英文句型的思考來回答,就是詞類不相符。
有了這個感覺,突然好像對我們時常稱呼 JavaScript 的名詞——程式語言——更加有所體悟。
為什麼會稱 JavaScript 為「語言」呢?相對的,HTML、CSS 就好像沒那麼常被這樣形容。就我穿鑿附會的想像,原因可能是:
JavaScript 和人類語言一樣,能夠歸納出固定的語法結構和規則
我想到的是,寫 JavaScript 時,之所以像是與瀏覽器溝通,也許就是表達式與陳述式相互作用的結果。陳述式提供句子的結構,控制語意的走向;而表達式則像是句子中的片語,可以產生一個具體的值,填補結構中的空白,才能賦予程式碼意義,進而向瀏覽器表達出我們的目的。
總結
以免被我的老人廢話給稀釋重點,最後再來複習一下表達式與陳述式的主要特色吧:
- 表達式
- 執行時會產生值
- 用於預期會是值的地方(插槽),例如 JSX 的大括號內、變數賦值,以及函式的參數等等
- 可以與其他表達式結合,產生更複雜的表達式
- 陳述式
- 告訴瀏覽器執行特定動作
- 形塑程式碼的結構,控制程式碼流程走向
- 經常與表達式一起使用,但本身不會產生值
希望這篇文章能夠幫助到你,如有錯誤也煩請指教,感謝你的閱讀。