前言
在程式語言中,運算子 (operator) 視為該語言基本的指令,通常不能再拆分成更小的單位。本文介紹 JavaScript 中可用的運算子。
自製斷言 (Assertion)
傳統上,程式語言的教材會用終端機輸出文字,再由程式撰寫者判讀。但這樣的方式並沒有充份利用到電腦程式自動化的功能,因為每次的判讀皆需要人工作業。
比較好的方式,是透過斷言 (assertion) 來判定程式是否錯誤。所謂的斷言,是用來確認某段程式是否正確的程式碼。雖然 JavaScript 沒有內建的斷言,我們可以自己寫一個簡易版的斷言。參考以下實例:
/* Home-made assertion. */
function assert(cond, msg) {
if (!(cond)) {
throw (msg ? msg : 'Assertion failed');
}
}
assert(1 + 1 === 2, "1 + 1 should be 2");
在本例中,assert
是一個函式,該函式在 cond
不為真時,會拋出 msg
字串並中止程式運行。斷言基本上就是執行這樣的任務,我們這裡用簡易的方法來實作斷言。雖然我們還沒正式學函式的寫法,這個函式很短,可以試著讀讀看。
代數運算子 (Arithmetic Operator)
代數運算子 (arithmetic operator) 用來進行基本的四則運算。ECMAScript 支援以下代數運算子:
+
:相加-
:相減*
:相乘/
:相除%
:取餘數+
:取正號-
:取負號++
:遞增 (加 1)--
:遞減(減 1)
代數運算子使用方式如同數學上的代數,像 2 + 3 * 5
會先運算 3 * 5
再加 2
,如果要改變其順序,可用中括號,像 (2 + 3) * 5
會先計算 (3 + 5)
再乘 5
。以下是使用代數運算子的實例:
/* Home-made assertion. */
function assert(cond, msg) {
if (!(cond)) {
throw (msg ? msg : 'Assertion failed');
}
}
assert(3 + 4 == 7, "Wrong value");
assert(3 - 4 == -1, "Wrong value");
assert(3 * 4 == 12, "Wrong value");
assert(3 / 4 == 0.75, "Wrong value");
assert(3 % 4 == 3, "Wrong value");
由於 JavaScript 沒有內建的斷言 (assertion),我們自製了一個簡易的版本。使用斷言取代終端機輸出,可以讓我們清楚地表達程式的意圖,也可讓程式自動進行檢查。
由於 ECMAScript 的數字是浮點數,相除時可能會得到有小數點的數,這時候,可視需求使用 Math.round() (四捨五入)、 Math.floor() (無條件捨去) 或是 Math.ceil() (無條件進位) 取至整數位。如下例:
/* Home-made assertion. */
function assert(cond, msg) {
if (!(cond)) {
throw (msg ? msg : 'Assertion failed');
}
}
assert(Math.round(4.5) === 5, "Wrong value");
assert(Math.floor(4.5) === 4, "Wrong value");
assert(Math.ceil(4.5) === 5, "Wrong value");
遞增或遞減是一種語法糖,基本上就是把 x = x + 1
簡化的語法。遞增/減可以前置或後置,會影響到變數的狀態,如下例:
/* Home-made assertion. */
function assert(cond, msg) {
if (!(cond)) {
throw (msg ? msg : 'Assertion failed');
}
}
let x = 4;
// DON'T DO THIS in production code.
let y = ++x + x++;
assert(x === 6, "Wrong value x");
assert(y === 10, "Wrong value y");
在這個例子中,x
遞增兩次,故得 6
。y
在 x
遞增一次後 (當下為 5
) 自身疊加一次,故得 10
。雖然這是合法的 JavaScript 程式,但閱讀此段程式碼的人往往需多想一下程式的狀態;筆者建議不要去強記前置或後置的規則,把遞增/減寫在單獨的一行比較好。
比較運算子 (Relational Operator)
比較運算子 (relational operator) 用來比較兩資料間的大小。以下是 JavaScript 的比較運算子:
===
:嚴格相等!==
:嚴格不等==
:相等!=
:不等>
:大於>=
:大於等於<
:小於<=
:小於等於
以下是實例:
/* Home-made assertion. */
function assert(cond, msg) {
if (!(cond)) {
throw (msg ? msg : 'Assertion failed');
}
}
assert(4 > 3, "Wrong relation");
assert(4 >= 3, "Wrong relation");
assert(3 === 3, "Wrong relation");
assert(5 !== 3, "Wrong relation");
assert(3 < 4, "Wrong relation");
assert(3 <= 4, "Wrong relation");
JavaScript 使用兩種不同的相等/不等運算子,一般情形下,建議用嚴格相等/不等運算子,因為另一種相等/不等運算子有著複雜的隱性轉換規則 (共 23 種),與其去強記那些複雜的規則不如用比較單純的嚴格相等/不等運算子。
字串運算子
JavaScript 用 +
做為字串相接的運算子:
+
:相接
在 ECMAScript 6 以後,可以直接用字串模板 (template literal) 取代大部分的字串相接運算子。搭配 Babel 的話,可以把字串模板轉為合法的 ES5 字串,不用手動串連字串。
邏輯運算子
邏輯運算是用來結合多個條件時使用,有以下運算子:
&&
:且 (and)||
:或 (or)!
:否 (not)
一般計算機概論的書籍會列真值表,不要特別去背誦,只要記住以下原則即可:
- 且 (and):所有條件皆為真時才為真
- 或 (or):只要其中一個條件為真時即為真
以下是實例:
/* Home-made assertion. */
function assert(cond, msg) {
if (!(cond)) {
throw (msg ? msg : 'Assertion failed');
}
}
// && (and)
assert((true && true) === true, "Wrong logic");
assert((true && false) === false, "Wrong logic");
assert((false && true) === false, "Wrong logic");
assert((false && false) === false, "Wrong logic");
// || (or)
assert((true || true) === true, "Wrong logic");
assert((true || false) === true, "Wrong logic");
assert((false || true) === true, "Wrong logic");
assert((false || false) === false, "Wrong logic");
// ! (not)
assert((!true) === false, "Wrong logic");
assert((!false) === true, "Wrong logic");
三元運算子 (Ternary Operator)
三元運算子是較簡短的 if
... else
... 語法,主要用於一行內的短敘述:
- (condition)
?
..:
..
由於三元運算子是表達式 (expression),會回傳值。以下是實例:
/* Home-made assertion. */
function assert(cond, msg) {
if (!(cond)) {
throw (msg ? msg : 'Assertion failed');
}
}
let n = 5 > 3 ? 5 : 3;
assert(n === 5, `Wrong value: ${n}`);
二元運算運算子 (Bitwise Operators)
以下是 JavaScript 的二位元運算子:
- bitwise and
&
- bitwise or
|
- bitwise xor
^
- bitwise not
~
- left shift
<<
- right shift
>>
- zero-fill right shift
>>>
雖然二位元運算對一般讀者來說比較陌生,但適度地運用二位元運算可節省運算時間,以下是一個例子:
/* Home-made assertion. */
function assert(cond, msg) {
if (!(cond)) {
throw (msg ? msg : 'Assertion failed');
}
}
let a = 3; // 0000 0011
let b = 5; // 0000 0101
/* 0000 0011
&) 0000 0101
-------------
0000 0001 */
assert((a & b) === 1, "3 & 5 should be 1");
/* 0000 0011
|) 0000 0101
-------------
0000 0111 */
assert((a | b) === 7, "3 | 5 should be 7");
/* 0000 0011
^) 0000 0101
-------------
0000 0110 */
assert((a ^ b) === 6, "3 ^ 5 should be 6");
二元運算在日常生活中不會用到,有些讀者可能會對二元運算感到陌生,筆者在註解處寫下運算過程,供讀者參考。如果讀者想學二元運算,可以翻閱計算機概論等書籍,此處不詳談。
型別檢查運算子
JavaScript 使用 typeof 做為型別運算子,typeof
會回傳一個字串,該字串表示資料的型別,可能的回傳值如下:
"undefined"
"boolean"
"number"
"string"
- (新)
"symbol"
"function"
"object"
由於 JavaScript 的物件系統是以原型 (prototype) 為基礎,物件沒有類別 (class) 的概念,我們無法用 typeof
來檢查物件的型別,程式設計者需要改變對物件的思維。我們將於後文說明 JavaScript 的物件系統。
指派運算子 (Assignment Operators)
指派運算子算是一種語法糖,將 x = x + 1
簡化為 x += 1
,大部分的代數運算子和二元運算子都有相對應的指派運算子:
+=
-=
*=
/=
%=
<<=
>>=
>>>=
&=
|=
^=
(新) 解構運算 (Destructing Assignment)
解構運算是 ECMAScript 6 之後所加入的新功能,主要是簡化從物件中取出資料的過程;雖然不是必備的特性,但的確可以縮減一些程式碼,如下例:
/* Home-made assertion. */
function assert(cond, msg) {
if (!(cond)) {
throw (msg ? msg : 'Assertion failed');
}
}
const obj = {b: 2, c: 3, d: 4};
// Destructing assignment.
const {a, b, c} = obj;
assert(typeof a === "undefined", "a should be undefined");
assert(b === 2, "b should be 2");
assert(c === 3, "c should be 3");
在這個例子中,a
沒有值,故為未定義的 (undefined),b
和 c
則各自指派到特定值。