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 。
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 中的异步功能。