逗號運算子 (,)

Baseline 已廣泛支援

此特性已相當成熟,可在許多裝置和瀏覽器版本上使用。自 ⁨2015 年 7 月⁩以來,各瀏覽器均已提供此特性。

逗號 (,) 運算子會依次評估其每個運算元(從左到右),並返回最後一個運算元的值。這通常用於為 for 迴圈的後置條件提供多個更新表示式。

試一試

let x = 1;

x = (x++, x);

console.log(x);
// Expected output: 2

x = (2, 3);

console.log(x);
// Expected output: 3

語法

js
expr1, expr2, expr3/* , … */

引數

expr1, expr2, expr3, …

一個或多個表示式,其中最後一個表示式的值將作為複合表示式的值返回。

描述

當你想在需要單個表示式的位置包含多個表示式時,可以使用逗號運算子。該運算子最常見的用法是在 for 迴圈中提供多個更新表示式。對於在需要單個表示式的位置允許包含多個*語句*的慣用法,你可以使用 IIFE

由於除最後一個表示式外,所有表示式都經過評估然後被丟棄,因此這些表示式必須具有副作用才能發揮作用。常見的具有副作用的表示式包括賦值、函式呼叫以及 ++-- 運算子。如果它們呼叫 getter 或觸發型別強制轉換,其他表示式也可能具有副作用。

逗號運算子在所有運算子中具有最低的優先順序。如果你想將一個逗號連線的表示式嵌入到更大的表示式中,你必須用括號將其括起來。

逗號運算子與在其他位置用作語法分隔符的逗號完全不同,這些用法包括:

  • 陣列初始化器中的元素 ([1, 2, 3])
  • 物件初始化器中的屬性 ({ a: 1, b: 2 })
  • 函式宣告/表示式中的引數 (function f(a, b) { … })
  • 函式呼叫中的實參 (f(1, 2))
  • 繫結列表中,在 let, const, 或 var 宣告中 (const a = 1, b = 2;)
  • import 宣告中的匯入列表 (import { a, b } from "c";)
  • export 宣告中的匯出列表 (export { a, b };)

事實上,雖然這些地方有些幾乎接受所有表示式,但它們不接受逗號連線的表示式,因為這會與語法逗號分隔符產生歧義。在這種情況下,你必須用括號將逗號連線的表示式括起來。例如,以下是一個聲明瞭兩個變數的 const 宣告,其中逗號不是逗號運算子:

js
const a = 1, b = 2;

這與下面的情況不同,在下面的例子中,b = 2 是一個賦值表示式,而不是宣告。a 的值是 2,即賦值的返回值,而 1 的值被丟棄了。

js
const a = (1, b = 2);

逗號運算子不能作為尾隨逗號出現。

示例

在 for 迴圈中使用逗號運算子

如果 a 是一個兩維陣列,每邊有 10 個元素,下面的程式碼使用逗號運算子同時增加 i 和減少 j,從而列印陣列中對角線元素的值:

js
const a = Array.from({ length: 10 }, () =>
  Array.from({ length: 10 }, Math.random),
); // A 10×10 array of random numbers

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

使用逗號運算子連線賦值

由於逗號具有最低的優先順序——甚至低於賦值——逗號可以用於連線多個賦值表示式。在下面的例子中,a 被設定為 b = 3 的值(即 3)。然後,c = 4 表示式被評估,其結果成為整個逗號表示式的返回值。

js
let a, b, c;

a = b = 3, c = 4; // Returns 4
console.log(a); // 3 (left-most)

let x, y, z;

x = (y = 5, z = 6); // Returns 6
console.log(x); // 6 (right-most)

處理然後返回

另一個可以用逗號運算子實現的例子是先處理後返回。如前所述,只有最後一個元素會被返回,但所有其他元素也都會被評估。所以,可以這樣做:

js
function myFunc() {
  let x = 0;

  return (x += 1, x); // the same as return ++x;
}

這對於單行箭頭函式特別有用。下面的例子使用一個map() 來獲取陣列的總和以及其元素的平方,否則這將需要兩次迭代,一次使用 reduce(),一次使用 map()

js
let sum = 0;
const squares = [1, 2, 3, 4, 5].map((x) => ((sum += x), x * x));
console.log(squares); // [1, 4, 9, 16, 25]
console.log(sum); // 15

丟棄引用繫結

逗號運算子總是將最後一個表示式作為*值*而不是*引用*返回。這導致一些上下文資訊,例如 this 繫結丟失。例如,屬性訪問返回對函式的引用,該引用還記住它所訪問的物件,以便正確呼叫該屬性。如果方法是從逗號表示式返回的,則該函式將作為新的函式值被呼叫,並且 this 將是 undefined

js
const obj = {
  value: "obj",
  method() {
    console.log(this.value);
  },
};

obj.method(); // "obj"
(obj.method)(); // "obj" (the grouping operator still returns the reference)
(0, obj.method)(); // undefined (the comma operator returns a new value)

你可以透過這種技術進入間接 eval,因為直接 eval 要求函式呼叫發生在對 eval() 函式的引用上。

js
globalThis.isDirectEval = false;

{
  const isDirectEval = true;
  console.log(eval("isDirectEval")); // true
  console.log((eval)("isDirectEval")); // true (the grouping operator still returns a reference to `eval`)
  console.log((0, eval)("isDirectEval")); // false (the comma operator returns a new value)
}

規範

規範
ECMAScript® 2026 語言規範
# sec-comma-operator

瀏覽器相容性

另見