Index

no-loop-func

禁止在循环语句中包含不安全引用的函数声明

在循环中编写函数往往会导致错误,这是由于函数在循环周围创建闭包的方式造成的。例如:

🌐 Writing functions within loops tends to result in errors due to the way the function creates a closure around the loop. For example:

for (var i = 0; i < 10; i++) {
    funcs[i] = function() {
        return i;
    };
}

在这种情况下,你会期望循环中创建的每个函数返回不同的数字。实际上,每个函数都返回 10,因为那是作用域中 i 的最后一个值。

🌐 In this case, you would expect each function created within the loop to return a different number. In reality, each function returns 10, because that was the last value of i in the scope.

letconst 缓解了这个问题。

for (let i = 0; i < 10; i++) {
    funcs[i] = function() {
        return i;
    };
}

在这种情况下,循环中创建的每个函数都按预期返回不同的数字。

🌐 In this case, each function created within the loop returns a different number as expected.

规则详情

🌐 Rule Details

引发此错误是为了高亮一段可能不会按你预期工作的代码,并且也可能表明对语言的工作原理存在误解。如果你不修复此错误,代码可能会照常运行而没有任何问题,但在某些情况下,它可能会表现出意想不到的行为。

🌐 This error is raised to highlight a piece of code that may not work as you expect it to and could also indicate a misunderstanding of how the language works. Your code may run without any problems if you do not fix this error, but in some situations it could behave unexpectedly.

此规则不允许在循环中使用任何包含不安全引用的函数(例如,对外部作用域中已修改变量的引用)。此规则会忽略立即调用函数表达式(IIFE),但不会忽略异步函数或生成器函数。

🌐 This rule disallows any function within a loop that contains unsafe references (e.g. to modified variables from the outer scope). This rule ignores IIFEs but not async or generator functions.

此规则的错误代码示例:

🌐 Examples of incorrect code for this rule:

在线运行
/*eslint no-loop-func: "error"*/

var i = 0;
while(i < 5) {
    var a = function() { return i; };
    a();

    i++;
}

var i = 0;
do {
    function a() { return i; };
    a();

    i++
} while (i < 5);

let foo = 0;
for (let i = 0; i < 10; ++i) {
    //Bad, `foo` is not in the loop-block's scope and `foo` is modified in/after the loop
    setTimeout(() => console.log(foo));
    foo += 1;
}

for (let i = 0; i < 10; ++i) {
    //Bad, `foo` is not in the loop-block's scope and `foo` is modified in/after the loop
    setTimeout(() => console.log(foo));
}
foo = 100;

var arr = [];

for (var i = 0; i < 5; i++) {
    arr.push((f => f)(() => i));
}

for (var i = 0; i < 5; i++) {
    arr.push((() => {
        return () => i;
    })());
}

for (var i = 0; i < 5; i++) {
    (function fun () {
        if (arr.includes(fun)) return i;
        else arr.push(fun);
    })();
}

符合此规则的正确代码示例:

🌐 Examples of correct code for this rule:

在线运行
/*eslint no-loop-func: "error"*/

var a = function() {};

for (var i=10; i; i--) {
    a();
}

for (var i=10; i; i--) {
    var a = function() {}; // OK, no references to variables in the outer scopes.
    a();
}

for (let i=10; i; i--) {
    var a = function() { return i; }; // OK, all references are referring to block scoped variables in the loop.
    a();
}

for (const i of foo) {
    var a = function() { return i; }; // OK, all references are referring to block scoped variables in the loop.
    a();
}

for (using i of foo) {
    var a = function() { return i; }; // OK, all references are referring to block scoped variables in the loop.
    a();
}

for (var i=10; i; i--) {
    const foo = getsomething(i);
    var a = function() { return foo; }; // OK, all references are referring to block scoped variables in the loop.
    a();
}

for (var i=10; i; i--) {
    using foo = getsomething(i);
    var a = function() { return foo; }; // OK, all references are referring to block scoped variables in the loop.
    a();
}

for (var i=10; i; i--) {
    await using foo = getsomething(i);
    var a = function() { return foo; }; // OK, all references are referring to block scoped variables in the loop.
    a();
}

var foo = 100;
for (let i=10; i; i--) {
    var a = function() { return foo; }; // OK, all references are referring to never modified variables.
    a();
}
//... no modifications of foo after this loop ...

var arr = [];

for (var i=10; i; i--) {
    (function() { return i; })();
}

for (var i = 0; i < 5; i++) {
    arr.push((f => f)((() => i)()));
}

for (var i = 0; i < 5; i++) {
    arr.push((() => {
        return (() => i)();
    })());
}

此规则还支持 TypeScript 类型语法。

🌐 This rule additionally supports TypeScript type syntax.

针对该规则的 正确 TypeScript 代码示例:

🌐 Examples of correct TypeScript code for this rule:

在线运行
/*eslint no-loop-func: "error"*/

type MyType = 1;
let someArray: MyType[] = [];
for (let i = 0; i < 10; i += 1) {
	someArray = someArray.filter((item: MyType) => !!item);
}

选项

🌐 Options

此规则没有选项。

🌐 This rule has no options.

已知限制

🌐 Known Limitations

规则无法识别函数实例是立即调用然后丢弃,还是可能存储以供以后使用。

🌐 The rule cannot identify whether the function instance is just immediately invoked and then discarded, or possibly stored for later use.

const foo = [1, 2, 3, 4];
var i = 0;

while(foo.some(e => e > i)){
    i += 1;
}

这里 some 方法会立即对数组中的每个元素执行回调函数,然后丢弃函数实例。该函数不会在循环迭代的作用域之外被存储或重用。所以,这将按预期工作。

🌐 Here the some method immediately executes the callback function for each element in the array and then discards the function instance. The function is not stored or reused beyond the scope of the loop iteration. So, this will work as intended.

eslint-disable 注释可以在这种情况下使用。

版本

此规则是在 ESLint v0.0.9 中引入。

资源