Object Instantiation Inside Loop

ID

javascript.object_instantiation_loops

Severity

low

Remediation Complexity

trivial

Remediation Risk

low

Remediation Effort

low

Resource

Efficiency

Language

JavaScript

Tags

efficiency, loops

Description

Reports new <Class>(…​) expressions that execute on every iteration of an enclosing loop. Allocating the same kind of object per iteration spends CPU on construction and forces the garbage collector to clean up short-lived instances; the standard fix is to hoist the construction out of the loop or reuse a single instance.

The rule is scoped: a new inside a function literal nested within the loop is not flagged, because the function is invoked on its own schedule (event handler, callback) and the allocation is not strictly per-iteration.

Rationale

// Bad — new formatter per iteration.
for (const value of values) {
  const formatter = new Intl.NumberFormat("en-US");
  out.push(formatter.format(value));
}

// Bad — new RegExp per row.
for (const row of rows) {
  const re = new RegExp(`^${row.prefix}`);
  if (re.test(row.value)) keep(row);
}

Intl.NumberFormat, RegExp, Map, Set, custom domain classes — each construction inside the loop pays the full setup cost on every pass.

Remediation

Hoist the construction out of the loop, or — when the constructor depends on the iteration value — cache the instance in a Map keyed by the input.

// Good — one formatter for the whole pass.
const formatter = new Intl.NumberFormat("en-US");
for (const value of values) {
  out.push(formatter.format(value));
}

// Good — cache constructions whose argument varies.
const reByPrefix = new Map();
for (const row of rows) {
  let re = reByPrefix.get(row.prefix);
  if (!re) {
    re = new RegExp(`^${row.prefix}`);
    reByPrefix.set(row.prefix, re);
  }
  if (re.test(row.value)) keep(row);
}

References