生成的JavaScript函数中的变量行为不符合预期

时间:2015-12-28 17:47:28

标签: javascript scoping

我在循环中生成函数时遇到了一些JavaScript问题。

我想要的:

我想要这个工作的方式是for循环,每次迭代生成一个名为doStuff + i的函数。例如,第一次迭代将生成doStuff1(),第二次迭代将生成doStuff2(),依此类推。函数本身(为了示例)只需打印i - 即doStuff1()打印1doStuff2()打印2等。

我得到的:

实际发生了什么,i并没有“坚持”这个功能。它成为全球范围的一部分或其他东西,所以每个功能都是10。如果您点击下面代码段中的第一个按钮,就可以看到这一点。

我试过的:

使用生成器功能

在示例二中,我尝试使用function*表示法来创建正确的函数生成器。我很确定我之前已经实现了它,但我之前从未使用过这种表示法,所以我可能会离开。如果是这种情况,请告诉我。

结果与示例2相同。

使用字符串而不是整数

例如三,我决定尝试使用字符串而不是整数,它可以工作!对于每次迭代,a都附加到一个字符串,所以当我按顺序运行生成的函数时,我会得到一个字母a的小金字塔。

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

由于我必须在示例3中的不同范围内定义stringOut变量,所以我决定尝试使用相同的数字(例如4),它再次起作用!这对我来说没有多大意义,因为在更高的范围内,似乎更容易遭受与例1和1相同的问题。 2。

我想知道的事:

  • 为什么示例3& 4工作,而1& 2不?编辑:我的代码坏了。它都不起作用。
  • 我是否正确使用function*生成器声明?
  • 最好的(最简单,最简洁,最易读)的方法是什么?我知道制作一系列函数可能是最好的,但在这种情况下这是不可能的。

示例代码:

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>

1 个答案:

答案 0 :(得分:2)

问题是你没有在循环中创建多个函数,它们都是函数的相同实例,共享相同的闭包(包括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

你怎么解决?通过添加一个额外的闭包,我称之为技术&#34;冻结闭合&#34;

&#13;
&#13;
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
&#13;
&#13;
&#13;

您也可以使用Function.bind

&#13;
&#13;
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
&#13;
&#13;
&#13;