生成的 JavaScript 函数中的变量未按预期运行

Variable in generated JavaScript function doesn't behave as expected

从循环内生成函数时,JavaScript 中的范围界定存在一些问题。

我想要的:

我希望它的工作方式是一个 for 循环,每次迭代都会生成一个名为 doStuff + i 的函数。例如,第一次迭代将生成 doStuff1(),第二次迭代将生成 doStuff2(),依此类推。函数本身(为了举例)只需要打印出 i——也就是说,doStuff1() 打印出 1doStuff2() 打印出 2,等等。

我得到的:

实际发生的情况是 i 不在函数中 'stick'。它成为全局范围的一部分或其他东西,所以每个函数都是 10。如果您单击下面代码段中的第一个按钮,您可以看到它。

我尝试过的:

使用生成器函数

在示例二中,我尝试使用 function* 符号来创建合适的函数生成器。我很确定我以前实现过它,但我以前从未使用过这种表示法,所以我可能离题太远了。如果是这样,请告诉我。

结果与示例 2 相同。

使用字符串而不是整数

例如三,我决定尝试使用一个字符串而不是一个整数,它起作用了!对于每次迭代,a 附加到一个字符串,因此当我按顺序 运行 生成的函数时,我得到一个漂亮的字母 a.

的小金字塔

在不同的地方声明和分配变量

因为我必须在示例 3 中的不同范围内定义 stringOut 变量,所以我决定在示例 4 中对数字进行相同的尝试,并且再次成功!这对我来说没有多大意义,因为在更高的范围内似乎更有可能遇到与示例 1 和 2 相同的问题。

我想知道的:

示例代码:

function test1() {
  document.getElementById("output").innerHTML = "#1 Output:";
  var myFunctions = [];

  for (var i = 0; i < 10; i++) {
    myFunctions[i] = function() {
      document.getElementById("output").innerHTML += "<br>" + i;
    }
  }

  for (var j = 0; j < 10; j++) {
    myFunctions[j]();
  }
}

function test2() {
  document.getElementById("output").innerHTML = "#2 Output:";
  window.test2Funcs = {};
  function* Generator() {
    var functionName = "doStuff";
    var number = 0;
    while (number < 10) {
    number++;
    yield {
        myFunction: function() {
        document.getElementById("output").innerHTML += "<br>" + number;
        },
        name: functionName + (number)
      }
    }
  }
  
  var generator = new Generator();
  for (var k = 0; k < 10; k++) {
    var out = generator.next().value;
    window.test2Funcs[out.name] = out.myFunction;
  }
 
  for (var l = 1; l < 11; l++) {
    func = "doStuff" + l;
    test2Funcs[func]();
  }
}

function test3() {
  document.getElementById("output").innerHTML = "#3 Output:";
  var myFunctions = [];
  var stringOut = "";
  for (var i = 0; i < 10; i++) {
    stringOut += "a"; // Edit. Moved from within function below.
    myFunctions[i] = function() {
      document.getElementById("output").innerHTML += "<br>" + stringOut;
    }
  }

  for (var j = 0; j < 10; j++) {
    myFunctions[j]();
  }
}

function test4() {
  document.getElementById("output").innerHTML = "#4 Output:";
  var myFunctions = [];
  var numOut = 0; // Edit. Used to be var numOut = "";
  for (var i = 0; i < 10; i++) {
    numOut++; // Edit. Moved from within function below.
    myFunctions[i] = function() {
      document.getElementById("output").innerHTML += "<br>" + numOut;
    }
  }

  for (var j = 0; j < 10; j++) {
    myFunctions[j]();
  }
}

document.getElementById("test1").addEventListener("click", test1);
document.getElementById("test2").addEventListener("click", test2);
document.getElementById("test3").addEventListener("click", test3);
document.getElementById("test4").addEventListener("click", test4);
<button id="test1">1st Attempt</button>
<button id="test2">2nd Attempt</button>
<button id="test3">3rd Attempt</button>
<button id="test4">4th Attempt</button>
<div id="output">
Output:
</div>

问题是您没有在循环中创建多个函数,它们都是函数的同一个实例,共享同一个闭包(包括 i)。在函数被调用时,i 是它退出循环时的值。

var funcs = [];
for (var i=0; i < 5; i++) {
   funcs.push(function() {
       console.log(i);
   });
}
// Here, i is 5
funcs[0](); // 5
funcs[1](); // 5
funcs[4](); // 5

如何解决?通过添加一个额外的闭包,我称该技术为 "freezing a closure"

function createHandler(val) {
  return function() {
    console.log(val);
  }
}
var funcs = [];
for (var i = 0; i < 5; i++) {
  // By calling a different function, a copy of i is passed to that function
  funcs.push(createHandler(i));
}
// Here, i is 5
funcs[0](); // 0
funcs[1](); // 1
funcs[4](); // 4

您也可以使用 Function.bind

var funcs = [];
for (var i = 0; i < 5; i++) {
  // By calling bind, a new function is created, with a new closure
  funcs.push(
    (function(val) {
      console.log(val);
    }).bind(null, i)
  );
}
// Here, i is 5
funcs[0](); // 0
funcs[1](); // 1
funcs[4](); // 4