逗号运算符 (,)

**逗号 (,)** 运算符会评估其每个操作数(从左到右),并返回最后一个操作数的值。这通常用于为 for 循环的后置部分提供多个更新器。

试一试

语法

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

参数

expr1, expr2, expr3, …

一个或多个表达式,最后一个表达式作为复合表达式的值返回。

描述

当您想要在需要单个表达式的代码位置包含多个表达式时,可以使用逗号运算符。此运算符最常见的用法是在 for 循环中提供多个更新器。

由于除了最后一个表达式之外,所有表达式都会被评估然后丢弃,因此这些表达式必须具有副作用才能有用。具有副作用的常见表达式包括赋值、函数调用以及 ++-- 运算符。如果其他表达式调用了 getter 或触发了 类型强制转换,它们也可能具有副作用。

逗号运算符具有所有运算符中最低的 优先级。如果要将逗号连接的表达式合并到更大的表达式中,则必须对其进行括号。

逗号运算符与用作语法分隔符的其他位置的逗号完全不同,这些位置包括

  • 数组初始化器中的元素 ([1, 2, 3])
  • 对象初始化器 中的属性 ({ a: 1, b: 2 })
  • 函数声明/表达式中的参数 (function f(a, b) { … })
  • 函数调用中的参数 (f(1, 2))
  • 绑定 列表在 letconstvar 声明中 (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 绑定)丢失。例如,属性访问返回对函数的引用,该引用还记住它所访问的对象,以便可以正确调用该属性。如果该方法从逗号表达式中返回,则该函数被调用就像它是新的函数值一样,并且 thisundefined

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 语言规范
# sec-comma-operator

浏览器兼容性

BCD 表格仅在浏览器中加载

另请参阅