Function Declared In Loop

ID

javascript.no_loop_func

Severity

low

Remediation Complexity

trivial

Remediation Risk

low

Remediation Effort

low

Resource

Efficiency

Language

JavaScript

Tags

closures, efficiency, loops

Description

Reports function literals (function expressions, arrow functions, named function declarations, async / generator forms) declared inside the body of a loop. Each iteration creates a fresh function, capturing the iteration’s locals; depending on the binding kind (var vs. let/const) the closure sees a stale or fresh value, and the per-iteration allocation cost compounds inside hot loops.

Rationale

// Bad — every iteration produces a new closure capturing `i`.
function bad(items) {
  for (let i = 0; i < items.length; i++) {
    const handler = () => items[i];
    register(handler);
  }
}

The closure-over-loop-variable hazard is the more famous bug: with var, every iteration’s closure ends up reading the same final value of the counter; with let it’s per-iteration but every callback gets a different one. Either way, the surprises usually outweigh the convenience.

Remediation

Hoist the function out of the loop, or pass per-iteration state explicitly:

// Good — function defined once, parameterised per iteration.
function ok(items) {
  const handler = (i) => items[i];
  for (let i = 0; i < items.length; i++) {
    register(handler.bind(null, i));
  }
}

References