試一試
let x = 1;
x = (x++, x);
console.log(x);
// Expected output: 2
x = (2, 3);
console.log(x);
// Expected output: 3
語法
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 宣告,其中逗號不是逗號運算子:
const a = 1, b = 2;
這與下面的情況不同,在下面的例子中,b = 2 是一個賦值表示式,而不是宣告。a 的值是 2,即賦值的返回值,而 1 的值被丟棄了。
const a = (1, b = 2);
逗號運算子不能作為尾隨逗號出現。
示例
在 for 迴圈中使用逗號運算子
如果 a 是一個兩維陣列,每邊有 10 個元素,下面的程式碼使用逗號運算子同時增加 i 和減少 j,從而列印陣列中對角線元素的值:
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 表示式被評估,其結果成為整個逗號表示式的返回值。
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)
處理然後返回
另一個可以用逗號運算子實現的例子是先處理後返回。如前所述,只有最後一個元素會被返回,但所有其他元素也都會被評估。所以,可以這樣做:
function myFunc() {
let x = 0;
return (x += 1, x); // the same as return ++x;
}
這對於單行箭頭函式特別有用。下面的例子使用一個map() 來獲取陣列的總和以及其元素的平方,否則這將需要兩次迭代,一次使用 reduce(),一次使用 map()。
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。
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() 函式的引用上。
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 |
瀏覽器相容性
載入中…