今天我們將要來繼續來提提運算子中其他的經典概念!
在邏輯運算子當中,經由 AND(&&
)與 OR(||
)所算出的值其實並非是布林值(Boolean),而是經由 短路邏輯 所算出來的數值。
延伸閱讀:短路邏輯(short circuit logic)
假如房間內裡面只有兩隻貓,且在你不清楚牠們的花色時……
- 若假設兩隻貓同時為「黑貓」,如果你看到房間有一隻「橘貓」,則「兩隻貓都為黑貓」的假設勢必不可能成立。
- 若假設兩隻貓其中一隻貓要是「黑貓」時,一開始看到了其中一隻貓為「黑貓」,那麼「其中一隻是黑貓」的假設就一定會成立。
在 JavaScript 中的邏輯運算子同樣也參考了短路邏輯,也就是說採用了邏輯運算子中的 AND(&&
)與 OR(||
) 後,假設勢必 不成立 或 一定成立 時,它便不會往下執行下去。
若 A 為假值時,那麼就不往後看 B 是否為真,直接拋出 A 值。
0 && 1; // 0
若 A 為真值時,接著拋出 B 值。
2 && 1; // 1
若 A 為真值時,無論後面值是什麼,都會直接拋出 A 值。
1 && 0; // 1
1 && 200; // 1
若 A 為假值時,接著拋出 B 值,
0 || 1; // 1
0 || 100; // 100
而藉由短路邏輯的特性,可以使我們在撰寫如 if
敘述句時,可以寫出更容易表達的內容。
var isCatHungry = true;
var feedTimeIsUp = false;
if (isCatHungry || feedTimeIsUp) {
findFood();
meow();
meow();
meow();
// ...
}
比如貓貓就是個餓了就貓來瘋的概念,只要餵飯時間到或它們餓了就會開始吵……
而至於另一個 NOT 運算子(!
)最主要是用來將值做布林轉換所使用,語意上可看作 NOT
概念來撰寫:
var isHungry = true;
if (!isHungry) {
doSomething();
} else {
eatFood();
}
透過上面例子可以看得出來多一層反面邏輯會使得在閱讀時需要停頓下來思考一下,因此實戰中若沒有必要,直接使用正面的邏輯來撰寫對於閱讀體驗來說會比較好:
var isHungry = true;
if (isHungry) {
eatFood();
} else {
doSomething();
}
接著要提的三元運算子,若熟悉之後可以大幅減少有些需要透過 if/else
來處理的程式碼區塊,而它的基本概念為:
條件式 ? 條件為真時執行區塊 : 條件為假時執行區塊
在實戰中我們可以用它來防止呼叫函式時未給予傳送引數時所引發的錯誤:
function createADTemplate(setting) {
var setting = setting ? setting : {}
setting.title = setting.title ? setting.title : '廣告放送'
setting.content = setting.content ? setting.content : '請洽 09xx-xxx-xxx'
return `
<div class="ad-wrapper">
<div class="ad-header>
<h1>${setting.title}</h1>
</div>
<div class="ad-content>
${setting.content}
</div>
</div>
`;
}
createADTemplate({
title: 'ithelp 鐵人賽',
content: '等你一起來參賽!'
})
createADTemplate() // 沒給予參數,內部自動補上對應的內容
當然你也可以在後續的執行區塊內串連三元運算式進去:
var result = Math.floor(Math.random() * 2) ? 'this'
: Math.floor(Math.random() * 2) ? 'or this'
: Math.floor(Math.random() * 2) ? 'or...'
: 'this!'
console.log(result) // ?
但透過上述的範例你可以看見當使用過多的三元運算子鑲嵌在一起的時候可讀性會大幅降低,如果條件式還不相同的話那簡直會是個災難。
所以即便我們知道串聯的特性,但若遇到條件過多的情況下,仍要回歸到 if/else
或 switch
等等敘述句來協助,甚至是重構成其他的方法來解決判斷上的問題而非過度依賴三元運算子來處理。
接著最後
介紹完運算子之後,最後要來提個運算子當中不可不知概念,運算子優先權(Operator Precedence)。
console.log( 1 + 2 * 3 / 4 ) // 這個值應該為多少?
簡單來說,運算子優先權就好比我們在算數的時候有句口訣叫做「先乘除,後加減」,而在 JavaScript 眾多的運算子當中自然而然必免不了運算順序上的問題。
且優先權依照高到低總共還可分為 20 多個等級,讀者可以參考 w3schools 上的最下方的優先度表格。
但不看表格大抵來說的話,如果要確保你算的值不受其他運算子干擾時,可以透過群組運算子 ()
來將內容包起來,由於群組運算子在運算子優先度中是最高的,所以我們可以視為 必定先算括號內部的值。
console.log( (2 + (2 * 3)) / 4 ); // => (2 + 6) / 4 => 8 / 4 => 2
接著,另外一個與運算子優先權相輔相成的概念則是 運算子相依性(Operator Associativity)。
剛才我們已經知道運算子會有運算上的優先順序了,那麼假如今天運算的優先順序相同時,我們就得依靠運算相依性來得知運算的方向。
例如賦值運算子(Assignment operator)屬於右相依性的運算子,也就是說它將會從右邊開始往左邊運算。
a = b = c = 5;
而我們已經知道 c = 5
也算是個運算式(expression),而這個運算式會返回賦值的數值 5
,因此上方的程式碼等同於:
a = b = 5;
依此類推到最左方的 a
為止,最後可以原先的程式碼看作是:
c = 5;
b = 5;
a = 5;
以上便是運算式與運算子的一些重點概念部分,接下來我們將藉由這些概念來看看 敘述式(statement) 到底有什麼內容!
目標: