试一试
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 |
浏览器兼容性
加载中…