In JavaScript, short-circuiting refers to the partial evaluation of expressions to avoid unnecessary computation. This blog post describes the short-circuiting operators and assignments in JavaScript and how to use them.
Short-circuiting operators only evaluate the right side of an expression when necessary.
For example, for the logical AND operator &&
, when the left side of the operator is false, the right side of the operator will not change the result. The result is false regardless of whether the right side is true or false. Therefore JavaScript skips the evaluation of the right side and uses the value from the left side.
There are three short-circuiting binary operators in JavaScript:
A
and B
can be any expression. Their evaluation might invoke function and getter calls that can contain complex calculations or have side effects.
Because A
and B
can return any value, the three short-circuiting operators evaluate and return any value, not just booleans.
This means that you can use the short-circuiting operators for providing defaults (||
and ??
), for checking nullish values (&&
, before the optional chaining operator ?.
was available), and for conditional rendering in React (&&
).
// default values
a = a || 123; // assigns 123 to a if a is falsy
b = b ?? 123; // assigns 123 to b if b is nullish// optional chaining with && ( .? is a modern alterative)
if (obj.m != null && obj.m() === '123') {
// ...
}// React: conditional rendering
return <>
{user != null &&
<div>Welcome back, ${user.name}!</div>
}
<>;
With ES2021, you can use short-circuiting operators in assignment expressions (&&=
, ||=
, and ??=
). Short-circuiting assignments are only carried out when the current variable or property value does not trigger short-circuiting. This behavior can help avoid unnecessary updates.
Here is an example(-Infinity
is truthy):
let a = 3;
let b = 3;
let c = 0;
let d = 0;a &&= -Infinity;
b ||= -Infinity;
c &&= -Infinity;
d ||= -Infinity;console.log(a); // -Infinity
console.log(b); // 3 (short-circuiting ||, because 3 is truthy)
console.log(c); // 0 (short-circuiting &&, because 0 is falsy)
console.log(d); // -Infinity
Short-circuiting assignments look very similar to regular assignments with short-circuiting operator expressions. One might think that they can be refactored as follows without changing the behavior:
a = a && x; /* becomes */ a &&= x;
a = a || x; /* becomes */ a ||= x;
a = a ?? x; /* becomes */ a ??= x;
However, when I was developing the safety evaluation for the
‘Push Operator into Assignment’ and ‘Pull Operator Out of Assignment’ refactorings in P42, I discovered that those refactorings can lead to behavioral changes in some situations.
Consider the following example:
class C {
constructor(name) {
this.name = name;
} get x() {
return this._x;
} set x(value) {
console.log(`set ${this.name}.x to ${value}`);
this._x = value;
}
}// nullish coalescing operator
const a = new C("a");
a.x = a.x ?? 3;
a.x = a.x ?? 4;
console.log(a.x)// nullish assignment
const b = new C("b");
b.x ??= 3;
b.x ??= 4;
console.log(b.x)
Surprisingly, moving the operator into the assignment changes what operations are executed:
// output for nullish coalescing operator
"set a.x to 3"
"set a.x to 3"
3// output for nullish assignment
"set b.x to 3"
3
While these minor differences won’t matter most of the time, it’s good to be aware of them for two reasons:
- they can cause breakages during refactoring because they change existing behavior
- they can be beneficial to reduce the number of operations in performance hotspots
Short-circuiting operators in JavaScript (&&
, ||
, ??
) evaluate their right-hand side expression only when necessary. Their assignment equivalents (&&=
, ||=
, ??=
) only update a value when the current value would
cause the execution of the right side of the short-circuiting operator.