The ECMAScript proposal “Logical assignment operators” (by Justin Ridgewell and Hemanth HM) introduces the following compound assignment operators:
a ||= b
a &&= b
a ??= b
JavaScript already has the following compound assignment operators:
+= -= *= /= %= **=
&= ^= |=
<<= >>= >>>=
Each of these assignment operators works as follows:
a op= b
a = a op b
Before we can examine the proposed operators, we have to take a brief detour and learn about short-circuiting.
The logical operator ||
, &&
, ??
all short-circuit – their second operands are only evaluated if their first operands don’t already determine the result:
Operator | Equivalent to |
---|---|
a ¦¦ b |
a ? a : b |
a && b |
!a ? a : b |
a ?? b |
a !== undefined && a !== null ? a : b |
Logical assignment operators work differently from other compound assignment operators:
Assignment operator | Equivalent to | Only assigns if a is |
---|---|---|
a ¦¦= b |
a ¦¦ (a = b) |
Falsy |
a &&= b |
a && (a = b) |
Truthy |
a ??= b |
a ?? (a = b) |
Nullish |
Why is a ||= b
equivalent to the following expression?
a || (a = b)
Why not to this expression?
a = a || b
The former expression has the benefit of short-circuiting: The assignment is only evaluated if a
evaluates to false
. Therefore, the assignment is only performed if it’s necessary. In contrast, the latter expression always performs an assignment.
??=
to add missing properties const books = [
{
isbn: '123',
},
{
title: 'ECMAScript Language Specification',
isbn: '456',
},
];
// Add property .title where it’s missing
for (const book of books) {
book.title ??= '(Untitled)';
}
assert.deepEqual(
books,
[
{
isbn: '123',
title: '(Untitled)',
},
{
title: 'ECMAScript Language Specification',
isbn: '456',
},
]);
Consider the following function which returns an expression spread out across multiple lines:
function canContainNumber(value) {
return typeof value === 'number'
|| typeof value === 'bigint'
|| typeof value === 'string'
;
}
assert.equal(canContainNumber(''), true);
assert.equal(canContainNumber(Symbol()), false);
This expression can be broken up as follows:
function canContainNumber(value) {
let result = false;
result ||= typeof value === 'number';
result ||= typeof value === 'bigint';
result ||= typeof value === 'string';
return result;
}
Note: There are other, probably better, ways to improve the initial code. E.g., a switch
statement.