面對 JavaScript 核心觀念時,總是感覺似懂非懂,於是就這樣不求甚解地繼續學習程式。但該面對的最終還是得面對,有些東西總會像鬼一樣,沒解決就會陰魂不散的回來,表達式 (expression) 與陳述式 (statement) 這兩個好兄弟就是其中之一。
還好有 Josh Comeau 深入淺出的文章,把我從五里霧中拉了出來,應 該 吧。這篇文章將以此基礎,就我的理解加其他輔助資料撰寫而成,如有錯誤還煩請指教。那就廢話少說開始吧。
如果要說表達式是什麼?最簡單的回答就是「能夠產生值的程式碼」,它可以是:
168
"string"
true
、false
["arr", "ray", "yee"]
{ type: "object" }
=
+
、-
、*
、/
、%
>
、<
、==
、===
||
、&&
、!
||
:回傳「布林值」(「或」的判斷),或「第一個真值」&&
:回傳「布林值」(「且」的判斷),或「第一個假值」!
:回傳「翻轉後的布林值結果」?
"expressions" :
"statements"// 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 中,我們會透過一種叫做 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>
);
}
一樣是條件邏輯,我們只能在大括號內放入表達式,為什麼呢?
拿 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 時,之所以像是與瀏覽器溝通,也許就是表達式與陳述式相互作用的結果。陳述式提供句子的結構,控制語意的走向;而表達式則像是句子中的片語,可以產生一個具體的值,填補結構中的空白,才能賦予程式碼意義,進而向瀏覽器表達出我們的目的。
以免被我的老人廢話給稀釋重點,最後再來複習一下表達式與陳述式的主要特色吧:
希望這篇文章能夠幫助到你,如有錯誤也煩請指教,感謝你的閱讀。