表示式和運算子

本章介紹 JavaScript 的表示式和運算子,包括賦值、比較、算術、位、邏輯、字串、三元等。

從宏觀層面看,**表示式**是一個有效的程式碼單元,它能解析為一個值。表示式有兩種型別:有副作用的(例如賦值)和純粹**求值**的。

表示式 `x = 7` 是第一種型別的示例。此表示式使用 `=` **運算子**將值七賦給變數 `x`。表示式本身求值為 `7`。

表示式 `3 + 4` 是第二種型別的示例。此表示式使用 `+` 運算子將 `3` 和 `4` 相加併產生一個值 `7`。然而,如果它最終不是更大結構(例如 `const z = 3 + 4` 這樣的變數宣告)的一部分,它的結果將立即被丟棄——這通常是程式設計師的錯誤,因為求值不會產生任何效果。

正如以上示例所示,所有複雜表示式都由**運算子**連線,例如 `=` 和 `+`。在本節中,我們將介紹以下運算子:

這些運算子連線由更高優先順序運算子或基本表示式之一組成的**運算元**。您還可以在參考資料中找到運算子和表示式的完整詳細列表。

運算子的**優先順序**決定了在求值表示式時它們的應用順序。例如:

js
const x = 1 + 2 * 3;
const y = 2 * 3 + 1;

儘管 `*` 和 `+` 的順序不同,但兩個表示式都將得到 `7`,因為 `*` 的優先順序高於 `+`,所以 `*` 連線的表示式將始終首先求值。您可以透過使用括號(它建立了一個分組表示式——基本表示式)來覆蓋運算子優先順序。要檢視完整的運算子優先順序表以及各種注意事項,請參閱運算子優先順序參考頁面。

JavaScript 既有**二元**運算子和**一元**運算子,還有一個特殊的三元運算子,即條件運算子。二元運算子需要兩個運算元,一個在運算子之前,一個在運算子之後:

operand1 operator operand2

例如,`3 + 4` 或 `x * y`。這種形式稱為**中綴**二元運算子,因為運算子位於兩個運算元之間。JavaScript 中的所有二元運算子都是中綴的。

一元運算子需要一個運算元,可以在運算子之前或之後:

operator operand
operand operator

例如,`x++` 或 `++x`。`運算子 運算元` 形式稱為**字首**一元運算子,`運算元 運算子` 形式稱為**字尾**一元運算子。`++` 和 `--` 是 JavaScript 中唯一的字尾運算子——所有其他運算子,如 `!`、`typeof` 等都是字首的。

賦值運算子

賦值運算子根據其右運算元的值,將其值賦給左運算元。簡單的賦值運算子是等號(`=`),它將右運算元的值賦給左運算元。也就是說,`x = f()` 是一個賦值表示式,將 `f()` 的值賦給 `x`。

還有複合賦值運算子,它們是下表中列出的操作的簡寫:

名稱 簡寫運算子 含義
賦值 x = f() x = f()
加法賦值 x += f() x = x + f()
減法賦值 x -= f() x = x - f()
乘法賦值 x *= f() x = x * f()
除法賦值 x /= f() x = x / f()
取餘賦值 x %= f() x = x % f()
指數賦值 x **= f() x = x ** f()
左移賦值 x <<= f() x = x << f()
右移賦值 x >>= f() x = x >> f()
無符號右移賦值 x >>>= f() x = x >>> f()
按位與賦值 x &= f() x = x & f()
按位異或賦值 x ^= f() x = x ^ f()
按位或賦值 x |= f() x = x | f()
邏輯與賦值 x &&= f() x && (x = f())
邏輯或賦值 x ||= f() x || (x = f())
空值合併賦值 x ??= f() x ?? (x = f())

賦值給屬性

如果表示式求值為一個物件,那麼賦值表示式的左側可以對該表示式的屬性進行賦值。例如:

js
const obj = {};

obj.x = 3;
console.log(obj.x); // Prints 3.
console.log(obj); // Prints { x: 3 }.

const key = "y";
obj[key] = 5;
console.log(obj[key]); // Prints 5.
console.log(obj); // Prints { x: 3, y: 5 }.

有關物件的更多資訊,請閱讀使用物件

如果表示式未求值為物件,則對該表示式的屬性賦值將不執行賦值:

js
const val = 0;
val.x = 3;

console.log(val.x); // Prints undefined.
console.log(val); // Prints 0.

嚴格模式下,上述程式碼會丟擲錯誤,因為無法為原始值分配屬性。

對不可修改的屬性或沒有屬性的表示式(`null` 或 `undefined`)的屬性賦值是錯誤的。

解構

對於更復雜的賦值,解構語法是 JavaScript 表示式,它允許使用與陣列和物件字面量構造映象的語法從陣列或物件中提取資料。

不使用解構,從陣列和物件中提取值需要多個語句:

js
const foo = ["one", "two", "three"];

const one = foo[0];
const two = foo[1];
const three = foo[2];

使用解構,您可以透過一個語句將多個值提取到不同的變數中:

js
const [one, two, three] = foo;

求值與巢狀

通常,賦值用於變數宣告(即與 `const``let``var` 結合使用)或作為獨立語句。

js
// Declares a variable x and initializes it to the result of f().
// The result of the x = f() assignment expression is discarded.
let x = f();

x = g(); // Reassigns the variable x to the result of g().

然而,與其他表示式一樣,`x = f()` 等賦值表示式會求值為一個結果值。儘管此結果值通常不被使用,但它可以被另一個表示式使用。

鏈式賦值或在其他表示式中巢狀賦值可能導致意外行為。因此,一些 JavaScript 樣式指南不鼓勵鏈式或巢狀賦值。儘管如此,賦值鏈和巢狀有時可能會發生,因此理解它們的工作原理很重要。

透過鏈式或巢狀賦值表示式,其結果本身可以賦給另一個變數。它可以被記錄,可以放入陣列字面量或函式呼叫中,等等。

js
let x;
const y = (x = f()); // Or equivalently: const y = x = f();
console.log(y); // Logs the return value of the assignment x = f().

console.log(x = f()); // Logs the return value directly.

// An assignment expression can be nested in any place
// where expressions are generally allowed,
// such as array literals' elements or as function calls' arguments.
console.log([0, x = f(), 0]);
console.log(f(0, x = f(), 0));

求值結果與上表中“含義”列中 `=` 號右側的表示式匹配。這意味著 `x = f()` 求值為 `f()` 的結果,`x += f()` 求值為結果和 `x + f()`,`x **= f()` 求值為結果冪 `x ** f()`,依此類推。

對於邏輯賦值,`x &&= f()`、`x ||= f()` 和 `x ??= f()`,返回值是邏輯操作的值,不帶賦值,因此分別是 `x && f()`、`x || f()` 和 `x ?? f()`。

當這些表示式在沒有括號或其他分組運算子(如陣列字面量)的情況下鏈式使用時,賦值表示式**從右到左分組**(它們是右結合),但它們**從左到右求值**。

請注意,對於除 `=` 本身之外的所有賦值運算子,結果值始終基於操作**之前**運算元的值。

例如,假設已宣告以下函式 `f` 和 `g` 以及變數 `x` 和 `y`:

js
function f() {
  console.log("F!");
  return 2;
}
function g() {
  console.log("G!");
  return 3;
}
let x, y;

考慮以下三個示例:

js
y = x = f();
y = [f(), x = g()];
x[f()] = g();

求值示例 1

`y = x = f()` 等同於 `y = (x = f())`,因為賦值運算子 `=` 是右結合的。但是,它從左到右求值:

  1. 賦值表示式 `y = x = f()` 開始求值。
    1. 此賦值左側的 `y` 求值為對名為 `y` 的變數的引用。
    2. 賦值表示式 `x = f()` 開始求值。
      1. 此賦值左側的 `x` 求值為對名為 `x` 的變數的引用。
      2. 函式呼叫 `f()` 向控制檯列印“F!”,然後求值為數字 `2`。
      3. `f()` 的結果 `2` 被賦給 `x`。
    3. 賦值表示式 `x = f()` 現在已完成求值;其結果是 `x` 的新值,即 `2`。
    4. 該結果 `2` 反過來也被賦給 `y`。
  2. 賦值表示式 `y = x = f()` 現在已完成求值;其結果是 `y` 的新值——恰好是 `2`。`x` 和 `y` 都被賦值為 `2`,控制檯已列印“F!”。

求值示例 2

`y = [ f(), x = g() ]` 也從左到右求值:

  1. 賦值表示式 `y = [ f(), x = g() ]` 開始求值。
    1. 此賦值左側的 `y` 求值為對名為 `y` 的變數的引用。
    2. 內部陣列字面量 `[ f(), x = g() ]` 開始求值。
      1. 函式呼叫 `f()` 向控制檯列印“F!”,然後求值為數字 `2`。
      2. 賦值表示式 `x = g()` 開始求值。
        1. 此賦值左側的 `x` 求值為對名為 `x` 的變數的引用。
        2. 函式呼叫 `g()` 向控制檯列印“G!”,然後求值為數字 `3`。
        3. `g()` 的結果 `3` 被賦給 `x`。
      3. 賦值表示式 `x = g()` 現在已完成求值;其結果是 `x` 的新值,即 `3`。該結果 `3` 成為內部陣列字面量中的下一個元素(在 `f()` 的 `2` 之後)。
    3. 內部陣列字面量 `[ f(), x = g() ]` 現在已完成求值;其結果是一個包含兩個值的陣列:`[ 2, 3 ]`。
    4. 該陣列 `[ 2, 3 ]` 現在被賦給 `y`。
  2. 賦值表示式 `y = [ f(), x = g() ]` 現在已完成求值;其結果是 `y` 的新值——恰好是 `[ 2, 3 ]`。`x` 現在被賦值為 `3`,`y` 現在被賦值為 `[ 2, 3 ]`,控制檯已列印“F!”,然後列印“G!”。

求值示例 3

`x[f()] = g()` 也從左到右求值。(此示例假設 `x` 已被賦給某個物件。有關物件的更多資訊,請閱讀使用物件。)

  1. 賦值表示式 `x[f()] = g()` 開始求值。
    1. 此賦值左側的屬性訪問 `x[f()]` 開始求值。
      1. 此屬性訪問中的 `x` 求值為對名為 `x` 的變數的引用。
      2. 然後函式呼叫 `f()` 向控制檯列印“F!”,然後求值為數字 `2`。
    2. 此賦值中的屬性訪問 `x[f()]` 現在已完成求值;其結果是變數屬性引用:`x[2]`。
    3. 然後函式呼叫 `g()` 向控制檯列印“G!”,然後求值為數字 `3`。
    4. 該 `3` 現在被賦給 `x[2]`。(此步驟僅在 `x` 被賦給一個物件時才會成功。)
  2. 賦值表示式 `x[f()] = g()` 現在已完成求值;其結果是 `x[2]` 的新值——恰好是 `3`。`x[2]` 現在被賦值為 `3`,控制檯已列印“F!”,然後列印“G!”。

避免賦值鏈

鏈式賦值或在其他表示式中巢狀賦值可能導致意外行為。因此,不鼓勵在同一語句中鏈式賦值

特別是,將變數鏈放在 `const``let``var` 語句中通常**不起作用**。只有最外層/最左側的變數會被宣告;賦值鏈中的其他變數**不會**被 `const`/`let`/`var` 語句宣告。例如:

js
const z = y = x = f();

此語句似乎聲明瞭變數 `x`、`y` 和 `z`。然而,它實際上只聲明瞭變數 `z`。`y` 和 `x` 要麼是對不存在變數的無效引用(在嚴格模式下),要麼更糟糕的是,在寬鬆模式下會隱式為 `x` 和 `y` 建立全域性變數

比較運算子

比較運算子比較其運算元,並根據比較結果是否為真返回一個邏輯值。運算元可以是數值、字串、邏輯或物件值。字串根據標準的字典順序(使用 Unicode 值)進行比較。在大多數情況下,如果兩個運算元型別不同,JavaScript 會嘗試將它們轉換為適合比較的型別。此行為通常會導致對運算元進行數值比較。比較中型別轉換的唯一例外是 `===` 和 `!==` 運算子,它們執行嚴格相等和不等比較。這些運算子在檢查相等性之前不會嘗試將運算元轉換為相容型別。下表描述了基於此示例程式碼的比較運算子:

js
const var1 = 3;
const var2 = 4;
比較運算子
運算子 描述 返回 true 的示例
相等 (==) 如果運算元相等,則返回 `true`。 3 == var1

"3" == var1

3 == '3'
不相等 (!=) 如果運算元不相等,則返回 `true`。 var1 != 4
var2 != "3"
嚴格相等 (===) 如果運算元相等且型別相同,則返回 `true`。另請參見 `Object.is`JS 中的相同性 3 === var1
嚴格不相等 (!==) 如果運算元型別相同但不相等,或型別不同,則返回 `true`。 var1 !== "3"
3 !== '3'
大於 (>) 如果左運算元大於右運算元,則返回 `true`。 var2 > var1
"12" > 2
大於或等於 (>=) 如果左運算元大於或等於右運算元,則返回 `true`。 var2 >= var1
var1 >= 3
小於 (<) 如果左運算元小於右運算元,則返回 `true`。 var1 < var2
"2" < 12
小於或等於 (<=) 如果左運算元小於或等於右運算元,則返回 `true`。 var1 <= var2
var2 <= 5

**注意:** `=>` 不是比較運算子,而是箭頭函式的表示法。

算術運算子

算術運算子以數值(字面量或變數)作為其運算元,並返回一個單一的數值。標準算術運算子是加法(`+`)、減法(`-`)、乘法(`*`)和除法(`/`)。這些運算子與在大多數其他程式語言中用於浮點數時的工作方式相同(特別要注意,除以零會產生`Infinity`)。例如:

js
1 / 2; // 0.5
1 / 2 === 1.0 / 2.0; // this is true

除了標準算術運算(`+`、`-`、`*`、`/`)外,JavaScript 還提供下表中列出的算術運算子:

算術運算子
運算子 描述 示例
餘數 (%) 二元運算子。返回兩個運算元相除的整數餘數。 12 % 5 返回 2。
增量 (++) 一元運算子。為其運算元加一。如果用作字首運算子(`++x`),則返回其運算元加一後的值;如果用作字尾運算子(`x++`),則返回其運算元加一之前的值。 如果 `x` 為 3,則 `++x` 將 `x` 設定為 4 並返回 4,而 `x++` 返回 3,然後才將 `x` 設定為 4。
減量 (--) 一元運算子。為其運算元減一。返回值與增量運算子類似。 如果 `x` 為 3,則 `--x` 將 `x` 設定為 2 並返回 2,而 `x--` 返回 3,然後才將 `x` 設定為 2。
一元負號 (-) 一元運算子。返回其運算元的負值。 如果 `x` 為 3,則 `-x` 返回 -3。
一元加號 (+) 一元運算子。嘗試將運算元轉換為數字,如果它不是數字的話。

`+"3"` 返回 `3`。

`+true` 返回 `1`。

冪運算子 (**) 計算 `base` 的 `exponent` 次冪,即 `base^exponent`。 `2 ** 3` 返回 `8`。
`10 ** -1` 返回 `0.1`。

位運算子

位運算子將其運算元視為一組 32 位(零和一),而不是十進位制、十六進位制或八進位制數。例如,十進位制數九的二進位制表示為 1001。位運算子對這些二進位制表示執行操作,但它們返回標準的 JavaScript 數值。

下表總結了 JavaScript 的位運算子。

運算子 用法 描述
按位與 a & b 在兩個運算元對應位都為 1 的每個位位置返回 1。
按位或 a | b 在兩個運算元對應位都為 0 的每個位位置返回 0。
按位異或 a ^ b 在對應位相同的每個位位置返回 0。[在對應位不同的每個位位置返回 1。]
按位非 ~ a 反轉其運算元的位。
左移 a << b 將 `a` 的二進位制表示左移 `b` 位,從右側移入零。
有符號右移 a >> b 將 `a` 的二進位制表示右移 `b` 位,丟棄移出的位。
零填充右移 a >>> b 將 `a` 的二進位制表示右移 `b` 位,丟棄移出的位,並從左側移入零。

位邏輯運算子

從概念上講,位邏輯運算子的工作方式如下:

  • 運算元被轉換為 32 位整數,並用一系列位(零和一)表示。超過 32 位的數字會丟棄其最高有效位。例如,以下超過 32 位的整數將被轉換為 32 位整數:

    Before: 1110 0110 1111 1010 0000 0000 0000 0110 0000 0000 0001
    After:                 1010 0000 0000 0000 0110 0000 0000 0001
    
  • 第一個運算元中的每個位都與第二個運算元中的相應位配對:第一位與第一位,第二位與第二位,依此類推。

  • 運算子應用於每對位,結果按位構造。

例如,九的二進位制表示是 1001,十五的二進位制表示是 1111。因此,當位運算子應用於這些值時,結果如下:

表示式 結果 二進位制描述
15 & 9 9 1111 & 1001 = 1001
15 | 9 15 1111 | 1001 = 1111
15 ^ 9 6 1111 ^ 1001 = 0110
~15 -16 ~ 0000 0000 … 0000 1111 = 1111 1111 … 1111 0000
~9 -10 ~ 0000 0000 … 0000 1001 = 1111 1111 … 1111 0110

請注意,所有 32 位都使用按位非運算子進行了反轉,並且最高有效位(最左側的位)設定為 1 的值表示負數(補碼錶示)。`~x` 求值與 `-x - 1` 求值相同。

位移位運算子

位移位運算子需要兩個運算元:第一個是要移位的數量,第二個指定第一個運算元要移位的位數。移位操作的方向由使用的運算子控制。

移位運算子將其運算元轉換為 32 位整數,並返回 `Number``BigInt` 型別的結果:具體來說,如果左運算元的型別是 `BigInt`,它們返回 `BigInt`;否則,它們返回 `Number`

移位運算子列於下表中。

位移位運算子
運算子 描述 示例
左移
(<<)
此運算子將第一個運算元向左移動指定的位數。左側移出的多餘位被丟棄。從右側移入零位。 `9<<2` 產生 36,因為 1001 向左移動 2 位變成 100100,即 36。
有符號右移 (>>) 此運算子將第一個運算元向右移動指定的位數。右側移出的多餘位被丟棄。從左側移入最左側位的副本。 `9>>2` 產生 2,因為 1001 向右移動 2 位變成 10,即 2。同樣,`-9>>2` 產生 -3,因為符號被保留。
零填充右移 (>>>) 此運算子將第一個運算元向右移動指定的位數。右側移出的多餘位被丟棄。從左側移入零位。 `19>>>2` 產生 4,因為 10011 向右移動 2 位變成 100,即 4。對於非負數,零填充右移和有符號右移產生相同的結果。

邏輯運算子

邏輯運算子通常與布林(邏輯)值一起使用;當它們一起使用時,它們返回一個布林值。然而,`&&`、`||` 和 `??` 運算子實際上返回其中一個指定運算元的值,因此如果這些運算子與非布林值一起使用,它們可能會返回一個非布林值。因此,它們更恰當地被稱為“值選擇運算子”。邏輯運算子在下表中描述。

邏輯運算子
運算子 用法 描述
邏輯與 (&&) expr1 && expr2 如果 `expr1` 可以轉換為 `false`,則返回 `expr1`;否則,返回 `expr2`。因此,當與布林值一起使用時,如果兩個運算元都為 true,則 `&&` 返回 `true`;否則,返回 `false`。
邏輯或 (||) expr1 || expr2 如果 `expr1` 可以轉換為 `true`,則返回 `expr1`;否則,返回 `expr2`。因此,當與布林值一起使用時,如果任一運算元為 true,則 `||` 返回 `true`;如果兩個都為 false,則返回 `false`。
空值合併運算子 (??) expr1 ?? expr2 如果 `expr1` 既不是 `null` 也不是 `undefined`,則返回 `expr1`;否則,返回 `expr2`。
邏輯非 (!) !expr 如果其單個運算元可以轉換為 `true`,則返回 `false`;否則,返回 `true`。

可以轉換為 `false` 的表示式的例子包括那些求值為 `null`、`0`、`0n`、`NaN`、空字串(`""`)或 `undefined` 的表示式。

以下程式碼展示了 `&&`(邏輯與)運算子的示例。

js
const a1 = true && true; // t && t returns true
const a2 = true && false; // t && f returns false
const a3 = false && true; // f && t returns false
const a4 = false && 3 === 4; // f && f returns false
const a5 = "Cat" && "Dog"; // t && t returns Dog
const a6 = false && "Cat"; // f && t returns false
const a7 = "Cat" && false; // t && f returns false

以下程式碼展示了 `||`(邏輯或)運算子的示例。

js
const o1 = true || true; // t || t returns true
const o2 = false || true; // f || t returns true
const o3 = true || false; // t || f returns true
const o4 = false || 3 === 4; // f || f returns false
const o5 = "Cat" || "Dog"; // t || t returns Cat
const o6 = false || "Cat"; // f || t returns Cat
const o7 = "Cat" || false; // t || f returns Cat

以下程式碼展示了 `??`(空值合併)運算子的示例。

js
const n1 = null ?? 1; // 1
const n2 = undefined ?? 2; // 2
const n3 = false ?? 3; // false
const n4 = 0 ?? 4; // 0

請注意 `??` 的工作方式類似於 `||`,但它僅在第一個表示式是“空值”(即`null``undefined`)時才返回第二個表示式。`??` 是比 `||` 更好的替代方案,用於為可能為 `null` 或 `undefined` 的值設定預設值,尤其是在像 `''` 或 `0` 這樣的值是有效值且不應應用預設值時。

以下程式碼展示了 `!`(邏輯非)運算子的示例。

js
const n1 = !true; // !t returns false
const n2 = !false; // !f returns true
const n3 = !"Cat"; // !t returns false

短路求值

由於邏輯表示式從左到右求值,它們會根據以下規則進行“短路”求值測試:

  • `falsy && anything` 短路求值為 falsy 值。
  • `truthy || anything` 短路求值為 truthy 值。
  • `nonNullish ?? anything` 短路求值為非空值。

邏輯規則保證這些求值始終正確。請注意,上述表示式中的*anything*部分未被求值,因此這樣做的任何副作用都不會生效。

BigInt 運算子

大多數可用於數字之間的運算子也可用於 `BigInt` 值。

js
// BigInt addition
const a = 1n + 2n; // 3n
// Division with BigInts round towards zero
const b = 1n / 2n; // 0n
// Bitwise operations with BigInts do not truncate either side
const c = 40000000000000000n >> 2n; // 10000000000000000n

一個例外是無符號右移 (>>>),它未為 BigInt 值定義。這是因為 BigInt 沒有固定寬度,因此嚴格來說它沒有“最高位”。

js
const d = 8n >>> 2n; // TypeError: BigInts have no unsigned right shift, use >> instead

BigInt 和數字不能相互替換——您不能在計算中混合使用它們。

js
const a = 1n + 2; // TypeError: Cannot mix BigInt and other types

這是因為 BigInt 既不是數字的子集也不是超集。BigInt 在表示大整數時具有比數字更高的精度,但不能表示小數,因此任何一方的隱式轉換都可能導致精度丟失。使用顯式轉換來表明您希望操作是數字操作還是 BigInt 操作。

js
const a = Number(1n) + 2; // 3
const b = 1n + BigInt(2); // 3n

您可以比較 BigInt 和數字。

js
const a = 1n > 2; // false
const b = 3 > 2n; // true

字串運算子

除了可用於字串值的比較運算子之外,連線運算子 (+) 將兩個字串值連線在一起,返回另一個字串,該字串是兩個運算元字串的並集。

例如,

js
console.log("my " + "string"); // console logs the string "my string".

簡寫賦值運算子 `+=` 也可以用於連線字串。

例如,

js
let myString = "alpha";
myString += "bet"; // evaluates to "alphabet" and assigns this value to myString.

條件(三元)運算子

條件(三元)運算子是 JavaScript 中唯一接受三個運算元的運算子。該運算子根據條件可以取兩個值之一。語法是:

js
condition ? val1 : val2

如果 `condition` 為真,則運算子的值為 `val1`。否則,其值為 `val2`。您可以在任何使用標準運算子的地方使用條件運算子。

例如,

js
const status = age >= 18 ? "adult" : "minor";

此語句在 `age` 大於或等於十八歲時將值“adult”賦給變數 `status`。否則,它將值“minor”賦給 `status`。

逗號運算子

逗號運算子 (`,`) 求值其兩個運算元並返回最後一個運算元的值。此運算子主要用於 `for` 迴圈內部,以允許每次迴圈更新多個變數。在不必要的地方使用它是糟糕的風格。通常,可以而且應該使用兩個單獨的語句代替。

例如,如果 `a` 是一個每邊有 10 個元素的二維陣列,以下程式碼使用逗號運算子一次更新兩個變數。該程式碼列印陣列中對角線元素的值:

js
const x = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
const a = [x, x, x, x, x];

for (let i = 0, j = 9; i <= j; i++, j--) {
  //                              ^
  console.log(`a[${i}][${j}]= ${a[i][j]}`);
}

一元運算子

一元運算是隻有一個運算元的運算。

delete

`delete` 運算子刪除物件的屬性。語法是:

js
delete object.property;
delete object[propertyKey];
delete objectName[index];

其中 `object` 是物件的名稱,`property` 是現有屬性,`propertyKey` 是指向現有屬性的字串或符號。

如果 `delete` 運算子成功,它將從物件中刪除該屬性。之後嘗試訪問它將得到 `undefined`。如果操作可能,`delete` 運算子返回 `true`;如果操作不可能,則返回 `false`。

js
delete Math.PI; // returns false (cannot delete non-configurable properties)

const myObj = { h: 4 };
delete myObj.h; // returns true (can delete user-defined properties)

刪除陣列元素

由於陣列只是物件,從技術上講,可以從它們中 `delete` 元素。然而,這被認為是一種不好的做法——儘量避免它。當您刪除陣列屬性時,陣列長度不受影響,其他元素也不會重新索引。為了實現這種行為,最好直接將元素覆蓋為 `undefined` 值。要實際運算元組,請使用各種陣列方法,例如`splice`

typeof

`typeof` 運算子返回一個字串,指示未求值運算元的型別。`operand` 是要返回其型別的字串、變數、關鍵字或物件。括號是可選的。

假設您定義了以下變數:

js
const myFun = () => 5 + 2;
const shape = "round";
const size = 1;
const foo = ["Apple", "Mango", "Orange"];
const today = new Date();

`typeof` 運算子為這些變數返回以下結果:

js
typeof myFun; // returns "function"
typeof shape; // returns "string"
typeof size; // returns "number"
typeof foo; // returns "object"
typeof today; // returns "object"
typeof doesntExist; // returns "undefined"

對於關鍵字 `true` 和 `null`,`typeof` 運算子返回以下結果:

js
typeof true; // returns "boolean"
typeof null; // returns "object"

對於數字或字串,`typeof` 運算子返回以下結果:

js
typeof 62; // returns "number"
typeof "Hello world"; // returns "string"

對於屬性值,`typeof` 運算子返回屬性包含的值的型別:

js
typeof document.lastModified; // returns "string"
typeof window.length; // returns "number"
typeof Math.LN2; // returns "number"

對於方法和函式,`typeof` 運算子返回以下結果:

js
typeof blur; // returns "function"
typeof parseInt; // returns "function"
typeof shape.split; // returns "function"

對於預定義物件,`typeof` 運算子返回以下結果:

js
typeof Date; // returns "function"
typeof Function; // returns "function"
typeof Math; // returns "object"
typeof Option; // returns "function"
typeof String; // returns "function"

void

`void` 運算子指定一個表示式要被求值而不返回一個值。`expression` 是一個要被求值的 JavaScript 表示式。圍繞表示式的括號是可選的,但為了避免優先順序問題,最好使用它們。

關係運算符

關係運算符比較其運算元,並根據比較結果是否為真返回一個布林值。

in

`in` 運算子如果指定屬性在指定物件中,則返回 `true`。語法是:

js
propNameOrNumber in objectName

其中 `propNameOrNumber` 是表示屬性名或陣列索引的字串、數字或符號表達式,`objectName` 是物件的名稱。

以下示例展示了 `in` 運算子的一些用法。

js
// Arrays
const trees = ["redwood", "bay", "cedar", "oak", "maple"];
0 in trees; // returns true
3 in trees; // returns true
6 in trees; // returns false
"bay" in trees; // returns false
// (you must specify the index number, not the value at that index)
"length" in trees; // returns true (length is an Array property)

// built-in objects
"PI" in Math; // returns true
const myString = new String("coral");
"length" in myString; // returns true

// Custom objects
const myCar = { make: "Honda", model: "Accord", year: 1998 };
"make" in myCar; // returns true
"model" in myCar; // returns true

instanceof

`instanceof` 運算子如果指定物件是指定物件型別,則返回 `true`。語法是:

js
object instanceof objectType

其中 `object` 是要針對 `objectType` 進行測試的物件,`objectType` 是表示型別的建構函式,例如 `Map``Array`

當您需要在執行時確認物件的型別時,請使用 `instanceof`。例如,在捕獲異常時,您可以根據丟擲的異常型別分支到不同的異常處理程式碼。

例如,以下程式碼使用 `instanceof` 來確定 `obj` 是否是 `Map` 物件。由於 `obj` 是 `Map` 物件,因此 `if` 塊內的語句將執行。

js
const obj = new Map();
if (obj instanceof Map) {
  // statements to execute
}

基本表示式

所有運算子最終都作用於一個或多個基本表示式。這些基本表示式包括識別符號字面量,但也有其他幾種型別。下面簡要介紹它們,並在各自的參考部分詳細描述其語義。

this

`this` 關鍵字通常在函式內部使用。一般來說,當函式作為方法附加到物件時,`this` 指的是呼叫該方法的物件。它就像一個傳遞給函式的隱藏引數。`this` 是一個求值為物件的表示式,因此您可以使用我們介紹的所有物件操作。

js
this["propertyName"];
this.propertyName;
doSomething(this);

例如,假設函式定義如下:

js
function getFullName() {
  return `${this.firstName} ${this.lastName}`;
}

現在我們可以將這個函式附加到一個物件,它在呼叫時將使用該物件的屬性:

js
const person1 = {
  firstName: "Chris",
  lastName: "Martin",
};

const person2 = {
  firstName: "Chester",
  lastName: "Bennington",
};

// Attach the same function
person1.getFullName = getFullName;
person2.getFullName = getFullName;

console.log(person1.getFullName()); // "Chris Martin"
console.log(person2.getFullName()); // "Chester Bennington"

分組運算子

分組運算子 `( )` 控制表示式中的求值優先順序。例如,您可以覆蓋先乘除後加減的預設順序,先進行加法運算。

js
const a = 1;
const b = 2;
const c = 3;

// default precedence
a + b * c; // 7
// evaluated by default like this
a + (b * c); // 7

// now overriding precedence
// addition before multiplication
(a + b) * c; // 9

// which is equivalent to
a * c + b * c; // 9

屬性訪問器

屬性訪問器語法使用點符號或方括號符號獲取物件的屬性值。

js
object.property;
object["property"];

使用物件指南詳細介紹了物件屬性。

可選鏈

可選鏈語法 (`?.`) 在物件已定義且非 `null` 時執行鏈式操作,否則短路操作並返回 `undefined`。這允許您對可能為 `null` 或 `undefined` 的值進行操作而不會導致 `TypeError`。

js
maybeObject?.property;
maybeObject?.[property];
maybeFunction?.();

new

您可以使用`new` 運算子建立使用者定義物件型別或內建物件型別的例項。使用 `new` 如下:

js
const objectName = new ObjectType(param1, param2, /* …, */ paramN);

super

`super` 關鍵字用於呼叫物件父級上的函式。例如,它在中呼叫父建構函式時很有用。

js
super(args); // calls the parent constructor.
super.functionOnParent(args);