JavaScript 支持一系列复合赋值运算符,允许程序员简洁地表达二元运算和赋值。目前,只支持数学或位运算。
一直以来,缺少将逻辑运算与赋值结合起来的能力。现在有了!JavaScript 现在支持使用新的运算符 &&=
、||=
和 ??=
进行逻辑赋值。
逻辑赋值运算符 #
在我们深入研究新的运算符之前,让我们回顾一下现有的复合赋值运算符。例如,lhs += rhs
的含义大致等同于 lhs = lhs + rhs
。这种粗略的等价性适用于所有现有的运算符 @=
,其中 @
代表一个二元运算符,如 +
或 |
。值得注意的是,严格来说,这只有在 lhs
是一个变量时才正确。对于像 obj[computedPropertyName()] += rhs
这样的表达式中更复杂的左侧,左侧只会被评估一次。
现在让我们深入研究新的运算符。与现有的运算符相比,当 @
是一个逻辑运算符时,lhs @= rhs
不代表 lhs = lhs @ rhs
:&&
、||
或 ??
。
// As an additional review, here is the semantics of logical and:
x && y
// → y when x is truthy
// → x when x is not truthy
// First, logical and assignment. The two lines following this
// comment block are equivalent.
// Note that like existing compound assignment operators, more complex
// left-hand sides are only evaluated once.
x &&= y;
x && (x = y);
// The semantics of logical or:
x || y
// → x when x is truthy
// → y when x is not truthy
// Similarly, logical or assignment:
x ||= y;
x || (x = y);
// The semantics of nullish coalescing operator:
x ?? y
// → y when x is nullish (null or undefined)
// → x when x is not nullish
// Finally, nullish coalescing assignment:
x ??= y;
x ?? (x = y);
短路语义 #
与它们的数学和位运算对应物不同,逻辑赋值遵循其各自逻辑运算的短路行为。它们只在逻辑运算会评估右侧时才执行赋值。
起初这可能看起来很令人困惑。为什么不像其他复合赋值那样无条件地赋值给左侧呢?
这种差异有一个很好的实际原因。当将逻辑运算与赋值结合起来时,赋值可能会导致一个副作用,该副作用应该根据该逻辑运算的结果有条件地发生。无条件地导致副作用会对程序的性能甚至正确性产生负面影响。
让我们用一个设置元素默认消息的函数的两个版本来具体说明这一点。
// Display a default message if it doesn’t override anything.
// Only assigns to innerHTML if it’s empty. Doesn’t cause inner
// elements of msgElement to lose focus.
function setDefaultMessage() {
msgElement.innerHTML ||= '<p>No messages<p>';
}
// Display a default message if it doesn’t override anything.
// Buggy! May cause inner elements of msgElement to
// lose focus every time it’s called.
function setDefaultMessageBuggy() {
msgElement.innerHTML = msgElement.innerHTML || '<p>No messages<p>';
}
注意:由于 innerHTML
属性被指定为返回空字符串而不是 null
或 undefined
,因此必须使用 ||=
而不是 ??=
。在编写代码时,请记住,许多 Web API 不使用 null
或 undefined
来表示空或不存在。
在 HTML 中,对元素上的 .innerHTML
属性进行赋值是破坏性的。内部子元素会被删除,从新分配的字符串解析的新子元素会被插入。即使新字符串与旧字符串相同,它也会导致额外的工作和内部元素失去焦点。出于不造成不必要的副作用的实际原因,逻辑赋值运算符的语义会短路赋值。
以以下方式思考与其他复合赋值运算符的对称性可能会有所帮助。数学和位运算符是无条件的,因此赋值也是无条件的。逻辑运算符是有条件的,因此赋值也是有条件的。
逻辑赋值支持 #
- Chrome: 从版本 85 开始支持
- Firefox: 从版本 79 开始支持
- Safari: 从版本 14 开始支持
- Node.js: 从版本 16 开始支持
- Babel: 支持