逗号运算符 (,)
**逗号 (,
)** 运算符会评估其每个操作数(从左到右),并返回最后一个操作数的值。这通常用于为 for
循环的后置部分提供多个更新器。
试一试
语法
expr1, expr2, expr3/* , … */
参数
expr1
,expr2
,expr3
, …-
一个或多个表达式,最后一个表达式作为复合表达式的值返回。
描述
当您想要在需要单个表达式的代码位置包含多个表达式时,可以使用逗号运算符。此运算符最常见的用法是在 for
循环中提供多个更新器。
由于除了最后一个表达式之外,所有表达式都会被评估然后丢弃,因此这些表达式必须具有副作用才能有用。具有副作用的常见表达式包括赋值、函数调用以及 ++
和 --
运算符。如果其他表达式调用了 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 语言规范 # sec-comma-operator |
浏览器兼容性
BCD 表格仅在浏览器中加载