no-loop-func

Disallow functions in loops that use a variable the loop changes

A function remembers the variables around it, not the values they had when it was created. If a loop creates functions that use a variable shared by all iterations, every one of them sees the last value by the time it's called.

A function is only reported if it uses a variable from outside itself that's assigned in the loop, after the loop, or in another function. A const and a let declared inside the loop are always safe.

Configure#

Off by default. To turn it on:

bunfig.toml
[lint.rules]
"no-loop-func" = "error" # or "warn"

Examples#

example.js1 problem
for (var i = 0; i < buttons.length; i++) {
  buttons[i].onclick = function () {
    select(i); // always buttons.length
  };
}
example.js1 problem
let page = 0;
while (page < pages.length) {
  tasks.push(() => download(pages[page]));
  page++;
}

Not reported. Each iteration gets its own i and page:

example.jsNo problems
for (let i = 0; i < buttons.length; i++) {
  buttons[i].onclick = function () {
    select(i);
  };
}

for (const page of pages) {
  tasks.push(() => download(page));
}

Not reported, because retries never changes once the loop has started:

example.jsNo problems
let retries = 3;
for (const page of pages) {
  tasks.push(() => download(page, retries));
}

Not reported, because the function is called on the spot and nowhere else:

example.jsNo problems
for (var i = 0; i < buttons.length; i++) {
  (function () {
    select(i);
  })();
}