循环内的JavaScript闭包–;简单实例

循环内的JavaScript闭包–;简单实例,javascript,loops,closures,Javascript,Loops,Closures,var funcs=[]; //让我们创建3个函数 对于(变量i=0;i

var funcs=[];
//让我们创建3个函数
对于(变量i=0;i<3;i++){
//并将其存储在funcs中
funcs[i]=函数(){
//每个都应记录其值。
console.log(“我的值:+i”);
};
}
对于(var j=0;j<3;j++){
//现在让我们每个人都来看看
funcs[j]();
}
试试:

var funcs=[];
对于(变量i=0;i<3;i++){
funcs[i]=(函数(索引){
返回函数(){
log(“我的值:+索引”);
};
}(i) );
}
对于(var j=0;j<3;j++){
funcs[j]();

}
问题是,每个匿名函数中的变量
i
都绑定到函数外部的同一个变量

ES6解决方案:
let
ECMAScript 6(ES6)引入了新的
let
const
关键字,这些关键字的作用域不同于基于
var
的变量。例如,在一个基于
let
索引的循环中,循环中的每个迭代都将有一个具有循环范围的新变量
i
,因此代码将按预期工作。有很多资源,但我推荐它作为一个很好的信息来源

for (let i = 0; i < 3; i++) {
  funcs[i] = function() {
    console.log("My value: " + i);
  };
}
其思想是,与
.forEach
循环一起使用的回调函数的每次调用都将是它自己的闭包。传递给该处理程序的参数是特定于迭代特定步骤的数组元素。如果在异步回调中使用,它将不会与在迭代的其他步骤中建立的任何其他回调冲突

如果您碰巧在jQuery中工作,
$。each()
函数为您提供了类似的功能


经典解决方案:闭包 您要做的是将每个函数内的变量绑定到函数外的一个单独的、不变的值:

function makeHelper(obj)
{
  return {
    inc: function(){obj.counter++;},
    get: function(){return obj.counter;}
  }; 
}

function makeCounters(num)
{
  for (var i = 0; i < num; i++)
  {
    var obj = {counter: 0};
    counters[i] = makeHelper(obj);
  }
}
var funcs = [];
function tempFunc(i){
    return function(){
        console.log("My value: " + i);
    };
}
for (var i = 0; i < 3; i++) {  
    funcs[i] = tempFunc(i);                                     
}
for (var j = 0; j < 3; j++) {
    funcs[j]();                        
}
var funcs=[];
函数createfunc(i){
返回函数(){
console.log(“我的值:+i”);
};
}
对于(变量i=0;i<3;i++){
funcs[i]=createfunc(i);
}
对于(var j=0;j<3;j++){
//现在让我们每个人都来看看
funcs[j]();

}
原始示例不起作用的原因是,在循环中创建的所有闭包都引用了同一帧。实际上,在一个对象上有3个方法,并且只有一个
i
变量。它们都输出了相同的值。

您需要了解的是javascript中变量的范围是基于函数的。这与使用块作用域的c#相比是一个重要的区别,只需将变量复制到for中的一个即可

将其包装在一个函数中,该函数将像apphacker的答案那样返回函数,因为变量现在具有函数作用域

还有一个let关键字而不是var,这将允许使用块范围规则。在这种情况下,在for中定义一个变量就可以了。也就是说,由于兼容性,let关键字不是一个实用的解决方案

var funcs={};
对于(变量i=0;i<3;i++){
让index=i;//添加这个
funcs[i]=函数(){
log(“我的值:+索引);//更改为副本
};
}
对于(var j=0;j<3;j++){
funcs[j]();

}
另一种说法是,函数中的
i
在执行函数时绑定,而不是在创建函数时绑定

创建闭包时,
i
是对外部范围中定义的变量的引用,而不是创建闭包时定义的变量的副本。将在执行时对其进行评估

大多数其他答案提供了通过创建另一个不会改变值的变量来解决问题的方法


我只是想加一个解释来澄清。对于一个解决方案,个人来说,我会选择哈托的方法,因为这是从这里的答案中最不言自明的方法。发布的任何代码都可以工作,但我会选择闭包工厂,而不是写一堆注释来解释为什么我要声明一个新变量(Freddy和1800)或使用奇怪的嵌入式闭包语法(apphacker)。

这是该技术的另一个变体,类似于Bjorn的(apphacker),这使您可以在函数中指定变量值,而不是将其作为参数传递,这有时可能更清楚:

var funcs=[];
对于(变量i=0;i<3;i++){
funcs[i]=(函数(){
var指数=i;
返回函数(){
log(“我的值:+索引”);
}
})();

}
这描述了在JavaScript中使用闭包的常见错误

函数定义了一个新的环境 考虑:

function makeCounter()
{
  var obj = {counter: 0};
  return {
    inc: function(){obj.counter ++;},
    get: function(){return obj.counter;}
  };
}

counter1 = makeCounter();
counter2 = makeCounter();

counter1.inc();

alert(counter1.get()); // returns 1
alert(counter2.get()); // returns 0
每次调用
makeCounter
{counter:0}
都会创建一个新对象。此外,还有一份新的
obj
也将创建以引用新对象。因此,
counter1
counter2
彼此独立

循环中的闭包 在循环中使用闭包是很棘手的

考虑:

var counters = [];

function makeCounters(num)
{
  for (var i = 0; i < num; i++)
  {
    var obj = {counter: 0};
    counters[i] = {
      inc: function(){obj.counter++;},
      get: function(){return obj.counter;}
    }; 
  }
}

makeCounters(2);

counters[0].inc();

alert(counters[0].get()); // returns 1
alert(counters[1].get()); // returns 1
这是因为函数作用域中的局部变量以及函数参数变量都是直接分配的
新的输入副本。

随着ES6现在得到广泛支持,这个问题的最佳答案已经改变。ES6为这种情况提供了
let
const
关键字。我们可以使用
let
设置一个循环范围变量,如下所示:

var funcs=[];
对于(设i=0;i<3;i++){
funcs[i]=函数(){
console.log(“我的值:+i”);
};

}
最简单的解决方案是

而不是使用:

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

for(var j =0; j<3; j++){
    funcs[j]();
}
这背后的思想是,用一个(立即调用的函数表达式)封装for循环的整个主体,并将
new_i
作为参数传递,并将其捕获为
i
var funcs = [];
for(var new_i =0; new_i<3; new_i++){
    (function(i){
        funcs[i] = function(){
            alert(i);
        }
    })(new_i);
}

for(var j =0; j<3; j++){
    funcs[j]();
}
for (var i = 0; i < 3; i++) {
    createfunc(i)();
}

function createfunc(i) {
    return function(){console.log("My value: " + i);};
}
funcs[i] = function() {            // and store them in funcs
    throw new Error("test");
    console.log("My value: " + i); // each should log its value.
};
funcs[i] = new function() {   
    var closedVariable = i;
    return function(){
        console.log("My value: " + closedVariable); 
    };
};
funcs = {};
for (var i = 0; i < 3; i++) {         
  funcs[i] = function inner() {        // function inner's scope contains nothing
    console.log("My value: " + i);    
  };
}
console.log(window.i)                  // test value 'i', print 3
funcs = {};
function outer(i) {              // function outer's scope contains 'i'
  return function inner() {      // function inner, closure created
   console.log("My value: " + i);
  };
}
for (var i = 0; i < 3; i++) {
  funcs[i] = outer(i);
}
console.log(window.i)          // print 3 still
[0,2,3].forEach(function(i){ console.log('My value:', i); });
// My value: 0
// My value: 2
// My value: 3
var funcs = Query.range(0,3).each(function(i){
     return  function() {
        console.log("My value: " + i);
    };
});
funcs.iterate(function(f){ f(); });
var funcs = [];

new Array(3).fill(0).forEach(function (_, i) { // creating a range
    funcs[i] = function() {            
        // now i is safely incapsulated 
        console.log("My value: " + i);
    };
});

for (var j = 0; j < 3; j++) {
    funcs[j](); // 0, 1, 2
}
var funcs = [];
for (var i = 0; i < 3; i++) {          // let's create 3 functions
    funcs[i] = function() {            // and store them in funcs
        console.log("My value: " + i); // each should log its value.
    };
}
for (var j = 0; j < 3; j++) {
    funcs[j]();                        // and now let's run each one to see
}
var funcs = [];
for (var i = 0; i < 3; i++) {          
    let index = i;
    funcs[i] = function() {            
        console.log("My value: " + index); 
    };
}
for (var j = 0; j < 3; j++) {
    funcs[j]();                        
}
var funcs = [];
function tempFunc(i){
    return function(){
        console.log("My value: " + i);
    };
}
for (var i = 0; i < 3; i++) {  
    funcs[i] = tempFunc(i);                                     
}
for (var j = 0; j < 3; j++) {
    funcs[j]();                        
}
Create variable `funcs` and assign it an empty array;  
Loop from 0 up until it is less than 3 and assign it to variable `i`;
    Push to variable `funcs` next function:  
        // Only push (save), but don't execute
        **Write to console current value of variable `i`;**

// First loop has ended, i = 3;

Loop from 0 up until it is less than 3 and assign it to variable `j`;
    Call `j`-th function from variable `funcs`:  
        **Write to console current value of variable `i`;**  
        // Ask yourself NOW! What is the value of i?
var funcs = [];
for (var i = 0; i < 3; i++) {          // let's create 3 functions
    funcs[i] = function(x) {            // and store them in funcs
        console.log("My value: " + x); // each should log its value.
    }.bind(null, i);
}
for (var j = 0; j < 3; j++) {
    funcs[j]();                        // and now let's run each one to see
}
var funcs = [];
for (let i = 0; i < 3; i++) {          // let's create 3 functions
    funcs[i] = function() {            // and store them in funcs
        console.log("My value: " + i); // each should log its value.
    };
}
for (let j = 0; j < 3; j++) {
    funcs[j]();                        // and now let's run each one to see
}
var funcs = [];
for (var i = 0; i < 3; i++) {     
  (funcs[i] = function() {         
    console.log("My value: " + i); 
  })(i);
}
var funcs = [];
for (var i = 0; i < 3; i++) {      // let's create 3 functions
  funcs[i] = curryShowValue(i);
}
for (var j = 0; j < 3; j++) {
  funcs[j]();                      // and now let's run each one to see
}

function curryShowValue(i) {
  return function showValue() {
    console.log("My value: " + i);
  }
}
<script>
   var funcs = [];
   for (var i = 0; i < 3; i++) {
     funcs[i] = function () {
        debugger;
        console.log("My value: " + i);
     };
   }
   console.log(funcs);
</script>
<script>
    var funcs = [];
    for (let i = 0; i < 3; i++) {
        funcs[i] = function () {
           debugger;
           console.log("My value: " + i);
        };
    }
    console.log(funcs);
</script>