请解释在循环中使用JavaScript闭包

CMS*_*ing 56 javascript closures scope loops

我已经阅读了关于循环内的闭包和闭包的一些解释.我很难理解这个概念.我有这样的代码:有没有办法尽可能地减少代码,因此闭包的概念可以更清晰.我很难理解i两个括号内的部分.谢谢

function addLinks () {
    for (var i=0, link; i<5; i++) {

        link = document.createElement("a");
        link.innerHTML = "Link " + i;


        link.onclick = function (num) {
            return function () {
                alert(num);
            };
        }(i);
        document.body.appendChild(link);

    }
}
window.onload = addLinks;
Run Code Online (Sandbox Code Playgroud)

sle*_*man 114

警告:长(ish)答案

这是直接从我在内部公司维基中写的文章中复制的:

问题:如何在循环中正确使用闭包?快速回答:使用功能工厂.

  for (var i=0; i<10; i++) {
    document.getElementById(i).onclick = (function(x){
      return function(){
        alert(x);
      }
    })(i);
  }
Run Code Online (Sandbox Code Playgroud)

或者更容易阅读的版本:

  function generateMyHandler (x) {
    return function(){
      alert(x);
    }
  }

  for (var i=0; i<10; i++) {
    document.getElementById(i).onclick = generateMyHandler(i);
  }
Run Code Online (Sandbox Code Playgroud)

这通常会让那些不熟悉javascript或函数式编程的人感到困惑.这是误解闭包的结果.

闭包不仅传递变量的值,甚至传递变量的引用.闭包捕获变量本身!以下代码说明了这一点:

  var message = 'Hello!';
  document.getElementById('foo').onclick = function(){alert(message)};
  message = 'Goodbye!';
Run Code Online (Sandbox Code Playgroud)

单击元素'foo'将生成一个警告框,其中包含以下消息:"Goodbye!".因此,在循环中使用简单闭包将最终使所有闭包共享相同的变量,并且该变量将包含在循环中分配给它的最后一个值.例如:

  for (var i=0; i<10; i++) {
    document.getElementById('something'+i).onclick = function(){alert(i)};
  }
Run Code Online (Sandbox Code Playgroud)

单击时所有元素将生成一个数字为10的警告框.事实上,如果我们现在i="hello";所有元素现在都会生成"hello"警报!变量i在十​​个函数中共享,与当前函数/范围/上下文相同.将其视为一种私有全局变量,只有所涉及的函数才能看到.

我们想要的是该变量的实例,或者至少是对变量的简单引用而不是变量本身.幸运的是,javascript已经有了一种传递引用(对象)或值(对于字符串和数字)的机制:函数参数!

当在javascript中调用函数时,该函数的参数如果是对象则通过引用传递,如果是字符串或数字则通过值传递.这足以打破闭包中的变量共享.

所以:

  for (var i=0; i<10; i++) {
    document.getElementById(i).onclick =
      (function(x){ /* we use this function expression simply as a factory
                       to return the function we really want to use: */

        /* we want to return a function reference
           so we write a function expression*/
        return function(){
          alert(x); /* x here refers to the argument of the factory function
                       captured by the 'inner' closure */
        }

      /* The brace operators (..) evaluates an expression, in this case this
         function expression which yields a function reference. */

      })(i) /* The function reference generated is then immediately called()
               where the variable i is passed */
  }
Run Code Online (Sandbox Code Playgroud)

  • "闭包不仅仅传递变量的值,甚至传递变量的引用.闭包捕获变量本身!" 我喜欢这个解释. (4认同)
  • 这是我见过的最好的闭合解释之一.晶莹剔透. (3认同)

gna*_*arf 10

我已经用JavaScript编程了很长时间,而且"循环闭包"是一个非常广泛的话题.我假设您正在讨论在(function(param) { return function(){ ... }; })(param);for循环内部使用的实践,以便在内部函数稍后执行时保留循环的"当前值"...

代码:

for(var i=0; i<4; i++) {
  setTimeout(
    // argument #1 to setTimeout is a function.
    // this "outer function" is immediately executed, with `i` as its parameter
    (function(x) {
      // the "outer function" returns an "inner function" which now has x=i at the
      // time the "outer function" was called
      return function() {  
        console.log("i=="+i+", x=="+x);
      };
    })(i) // execute the "closure" immediately, x=i, returns a "callback" function
  // finishing up arguments to setTimeout
  , i*100);
}
Run Code Online (Sandbox Code Playgroud)

输出:

i==4, x==0
i==4, x==1
i==4, x==2
i==4, x==3
Run Code Online (Sandbox Code Playgroud)

正如您在输出中看到的那样,所有内部回调函数都指向相同的i,但是,由于每个函数都有自己的"闭包",因此值x实际上存储为i外部函数执行时的任何值.

通常,当您看到此模式时,您将使用与参数相同的变量名称和外部函数的参数:(function(i){ })(i)例如.该函数内的任何代码(即使稍后执行,如回调函数)将i在您调用"外部函数"时引用.