Short-Circuiting in JavaScript

Lars Grammel
Lars Grammel
Published on November 25, 2021

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: &&, ||, and ??

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>
  }
<>;
Short-Circuiting Assignments: &&=, ||=, and ??=

Short-circuiting also applies if you use the above operators in assignment expressions (&&=, ||=, and ??=). When you use short-circuiting assignments, the assignment is only carried out when the current value does not lead to short-circuiting. This 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
Refactoring Assignments with Short-Circuiting Operators

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
Summary

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.