開放原始碼技術文件網 使用運算子 (Operator)

最後修改日期為 JUN 6, 2019

前言

在程式語言中,運算子 (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 遞增兩次,故得 6yx 遞增一次後 (當下為 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)

三元運算子是較簡短的 ifelse … 語法,主要用於一行內的短敘述:

  • (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),bc 則各自指派到特定值。

分享本文
Facebook Twitter LinkedIn LINE Skype EverNote GMail Yahoo Yahoo
追蹤本站
Facebook Facebook Twitter