逗号运算符 (,)

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

浏览器兼容性

另见