?yb? 2009-04-15 06:06 采纳率: 100%
浏览 498
已采纳

在循环中关闭 JavaScript-简单实例

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
}

It outputs this:

My value: 3
My value: 3
My value: 3

Whereas I'd like it to output:

My value: 0
My value: 1
My value: 2


The same problem occurs when the delay in running the function is caused by using event listeners:

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

… or asynchronous code, e.g. using Promises:

// Some async wait function
const wait = (ms) => new Promise((resolve, reject) => setTimeout(resolve, ms));

for(var i = 0; i < 3; i++){
  wait(i * 100).then(() => console.log(i)); // Log `i` as soon as each promise resolves.
}

What’s the solution to this basic problem?

</div>

转载于:https://stackoverflow.com/questions/750486/javascript-closure-inside-loops-simple-practical-example

  • 写回答

20条回答 默认 最新

  • 七度&光 2009-04-15 06:18
    关注

    Well, the problem is that the variable i, within each of your anonymous functions, is bound to the same variable outside of the function.

    Classic solution: Closures

    What you want to do is bind the variable within each function to a separate, unchanging value outside of the function:

    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++) {
        funcs[j]();                        // and now let's run each one to see
    }

    Since there is no block scope in JavaScript - only function scope - by wrapping the function creation in a new function, you ensure that the value of "i" remains as you intended.


    2015 Solution: forEach

    With the relatively widespread availability of the Array.prototype.forEach function (in 2015), it's worth noting that in those situations involving iteration primarily over an array of values, .forEach() provides a clean, natural way to get a distinct closure for every iteration. That is, assuming you've got some sort of array containing values (DOM references, objects, whatever), and the problem arises of setting up callbacks specific to each element, you can do this:

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

    The idea is that each invocation of the callback function used with the .forEach loop will be its own closure. The parameter passed in to that handler is the array element specific to that particular step of the iteration. If it's used in an asynchronous callback, it won't collide with any of the other callbacks established at other steps of the iteration.

    If you happen to be working in jQuery, the $.each() function gives you a similar capability.


    ES6 solution: let

    ECMAScript 6 (ES6), the newest version of JavaScript, is now starting to be implemented in many evergreen browsers and backend systems. There are also transpilers like Babel that will convert ES6 to ES5 to allow usage of new features on older systems.

    ES6 introduces new let and const keywords that are scoped differently than var-based variables. For example, in a loop with a let-based index, each iteration through the loop will have a new value of i where each value is scoped inside the loop, so your code would work as you expect. There are many resources, but I'd recommend 2ality's block-scoping post as a great source of information.

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

    Beware, though, that IE9-IE11 and Edge prior to Edge 14 support let but get the above wrong (they don't create a new i each time, so all the functions above would log 3 like they would if we used var). Edge 14 finally gets it right.

    </div>
    
    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论
查看更多回答(19条)

报告相同问题?

悬赏问题

  • ¥20 神经网络Sequential name=sequential, built=False
  • ¥16 Qphython 用xlrd读取excel报错
  • ¥15 单片机学习顺序问题!!
  • ¥15 ikuai客户端多拨vpn,重启总是有个别重拨不上
  • ¥20 关于#anlogic#sdram#的问题,如何解决?(关键词-performance)
  • ¥15 相敏解调 matlab
  • ¥15 求lingo代码和思路
  • ¥15 公交车和无人机协同运输
  • ¥15 stm32代码移植没反应
  • ¥15 matlab基于pde算法图像修复,为什么只能对示例图像有效