一个简单的实际例子——循环中的 JavaScript 闭包

var funcs = [];// let's create 3 functionsfor (var i = 0; i < 3; i++) {
  // and store them in funcs
  funcs[i] = function() {
    // each should log its value.
    console.log("My value: " + i);
  };}for (var j = 0; j < 3; j++) {
  // and now let's run each one to see
  funcs[j]();}

报告的结果如下:

My value: 3

My value: 3

My value: 3

然而我希望它输出:

My value: 0

My value: 1

My value: 2
当使用event listener导致函数运行的延迟时,也会出现同样的问题:

var buttons = document.getElementsByTagName("button");// let's create 3 functionsfor (var i = 0; i < buttons.length; i++) {
  // as event listeners
  buttons[i].addEventListener("click", function() {
    // each should log its value.
    console.log("My value: " + i);
  });}
<button>0</button><br /><button>1</button><br /><button>2</button>

... 或者异步代码,例如使用 Promises:

// Some async wait functionconst wait = (ms) => new Promise((resolve, reject) => setTimeout(resolve, ms));
for (var i = 0; i < 3; i++) {
  // Log `i` as soon as each promise resolves.
  wait(i * 100).then(() => console.log(i));}

解决方案是什么?

你把循环里面的var i=0改成let i =0;就解决了。

        var funcs = [];// let's create 3 functions
        for (var i = 0; i < 3; i++) {
        (function(){
        var j=i;
        funcs[j] = function() {
        console.log("My value: " + j);
        }
        }())

}
//      var funcs = [];// let's create 3 functions
//      for (let i = 0; i < 3; i++) {
//      
//  funcs[i] = function() {
//  console.log("My value: " + i);
//      }


}   
for (var k = 0; k < 3; k++) {
  funcs[k]();
        }
    </script>

两种方式都是可以的,希望帮助到你

问题是,每个匿名函数中的变量 i,都绑定到函数外部的同一个变量上。
经典解决方案: 闭包
你需要做的是将每个函数中的变量绑定到一个单独的、不变的值:

var funcs = [];
function createfunc(i) {
  return function() {
    console.log("My value: " + i);
  };}
for (var i = 0; i < 3; i++) {
  funcs[i] = createfunc(i);}
for (var j = 0; j < 3; j++) {
  // and now let's run each one to see
  funcs[j]();}

由于 JavaScript 中没有块作用域——只有函数作用域——通过将函数创建包装在一个新函数中,您可以确保“ i”的值保持您所希望的那样。
2015年的解决方案: forEach
由于 arry.prototype.foreach 函数(在2015年)相对广泛的可用性,值得注意的是,在那些主要涉及对一组值进行迭代的情况下,。 .forEach() 提供了一种干净、自然的方式来为每个迭代获得一个不同的闭包。 也就是说,假设您得到了某种包含值的数组(DOM 引用、对象等等) ,并且设置特定于每个元素的回调会出现问题,您可以这样做:

var someArray = [ /* whatever */ ];// ...
someArray.forEach(function(arrayElement) {
  // ... code code code for this one element
  someAsynchronousFunction(arrayElement, function() {
    arrayElement.doSomething();
  });});

方法使用的回调函数的每次调用。 Foreach 循环将是它自己的闭包。 传递给该处理程序的参数是特定于迭代的特定步骤的数组元素。 如果它在异步回调中使用,它将不会与迭代的其他步骤中建立的任何其他回调冲突。
如果你正好在用 jQuery 工作,$。 每个()函数都提供了类似的功能。
ES6 方法: let让语句
Ecmascript 6(ES6)引入了新的let 和const 关键字,它们的作用域与基于 var 的变量不同。 例如,在具有基于 let 的索引的循环中,循环中的每个迭代都会有一个新值 i,其中每个值的作用域都在循环中,因此您的代码可以按照预期的方式工作。 这里有很多资源,但我推荐2ality 的块作用域文章作为一个很好的信息来源。

for (let i = 0; i < 3; i++) {
  funcs[i] = function() {
    console.log("My value: " + i);
  };}

试试这个:

var funcs = [];
    for (var i = 0; i < 3; i++) {
    funcs[i] = (function(index) {
        return function() {
            console.log("My value: " + index);
        };
    }(i));}
for (var j = 0; j < 3; j++) {
    funcs[j]();}

就我个人而言,我认为@aust 更近期关于使用。 现在.bind是最好的办法了。 还有下划线 / 下划线。 当你不需要或者不想搞乱 bind的 thisArg 时,可以使用partial 。

还能用这个Function.prototype.bind

var funcs = {};for (var i = 0; i < 3; i++) {
  funcs[i] = function(x) {
    console.log('My value: ' + x);
  }.bind(this, i);}for (var j = 0; j < 3; j++) {
  funcs[j]();}

通过首先在外部循环创建函数,然后在内部循环绑定结果,可以获得更好的性能。

function log(x) {
  console.log('My value: ' + x);}
var funcs = [];
for (var i = 0; i < 3; i++) {
  funcs[i] = log.bind(this, i);}
for (var j = 0; j < 3; j++) {
  funcs[j]();}

使用 Immediately-Invoked Function Expression
,,最简单和最易读的方式来囊括一个索引变量:

for (var i = 0; i < 3; i++) {
    (function(index) {
        console.log('iterator: ' + index);
        //now you can also loop an ajax call here 
        //without losing track of the iterator value:   $.ajax({}); 
    })(i);
}

这会将迭代器 i 发送到我们定义为索引的匿名函数中。 这将创建一个闭包,其中保存的变量 i 将用于 IIFE 中的异步功能。