Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/javascript/465.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Javascript臭名昭著的循环问题?_Javascript_Closures - Fatal编程技术网

Javascript臭名昭著的循环问题?

Javascript臭名昭著的循环问题?,javascript,closures,Javascript,Closures,我有以下代码片段 function addLinks () { for (var i=0, link; i<5; i++) { link = document.createElement("a"); link.innerHTML = "Link " + i; link.onclick = function () { alert(i); }; document.body.appen

我有以下代码片段

function addLinks () {
    for (var i=0, link; i<5; i++) {
        link = document.createElement("a");
        link.innerHTML = "Link " + i;
        link.onclick = function () {
            alert(i);
        };
        document.body.appendChild(link);
    }
}
上面的代码用于生成5个链接,并用警报事件绑定每个链接以显示当前链接id。但它不起作用。当你点击生成的链接,他们都说链接5

但是下面的代码片段按照我们的预期工作

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);
    }
}
以上两段代码引用自。按照作者的解释,似乎是闭包创造了奇迹

但它是如何工作的,以及闭包是如何让它工作的,这一切都超出了我的理解。为什么第一个不起作用而第二个起作用?有人能详细解释一下这个魔法吗


谢谢。

在第一个示例中,您只需将此函数绑定到onclick事件:

function() {alert(i);};
这意味着在单击事件上,js应该提醒addlink函数i变量的值。由于for循环,其值将为5

在第二个示例中,生成一个要与另一个函数绑定的函数:

function (num) {
  return function () { alert(num); };
}
这意味着:如果使用值调用,则返回一个函数,该函数将提醒输入值。例如,调用function3将返回函数{alert3}

在每次迭代中使用值i调用此函数,因此为每个链接创建单独的onclick函数

要点是,在第一个示例中,您的函数包含一个变量引用,而在第二个示例中,借助外部函数,您将引用替换为实际值。这大致上被称为闭包,因为您将变量的当前值包含在函数中,而不是保留对它的引用。

有关第一个示例的解释:

JavaScript的作用域是函数级的,而不是块级的,创建闭包只意味着将封闭作用域添加到封闭函数的词法环境中

循环终止后,函数级变量i的值为5,这是内部函数“看到”的值

在第二个示例中,对于每个迭代步骤,外部函数文字将计算为一个新的函数对象,该函数对象具有自己的作用域和局部变量num,其值设置为当前值i。由于num永远不会被修改,它将在闭包的生命周期内保持不变:下一个迭代步骤不会覆盖旧值,因为函数对象是独立的


请记住,这种方法效率很低,因为必须为每个链接创建两个新函数对象。这是不必要的,因为如果使用DOM节点进行信息存储,它们很容易共享:

function linkListener() {
    alert(this.i);
}

function addLinks () {
    for(var i = 0; i < 5; ++i) {
        var link = document.createElement('a');
        link.appendChild(document.createTextNode('Link ' + i));
        link.i = i;
        link.onclick = linkListener;
        document.body.appendChild(link);
    }
}

基本上,在第一个示例中,您将onclick处理程序内部的i直接绑定到onclick处理程序外部的i。因此,当onclick处理程序外部的i发生变化时,onclick处理程序内部的i也会发生变化


在第二个示例中,不是将其绑定到onclick处理程序中的num,而是将其传递到一个函数中,然后该函数将其绑定到onclick处理程序中的num。当您将其传递到函数中时,i的值被复制,而不是绑定到num。因此,当i更改时,num保持不变。之所以会发生复制,是因为JavaScript中的函数是闭包,这意味着一旦有东西被传递到函数中,它就会被关闭以进行外部修改。

其他人已经解释了发生了什么,这里有一个替代解决方案

function addLinks () {
  for (var i = 0, link; i < 5; i++) {
    link = document.createElement("a");
    link.innerHTML = "Link " + i;

    with ({ n: i }) {
      link.onclick = function() {
        alert(n);
      };
    }
    document.body.appendChild(link);
  }
}

基本上是穷人让装订。

我喜欢为厚脸皮的人写简单的解释,因为我厚脸皮,所以就这样

我们在页面上有5个div,每个div都有一个ID。。。第一组,第二组,第三组,第四组,第五组

jQuery可以做到这一点

for (var i=1; i<=5; i++) {
    $("#div" + i).click ( function() { alert ($(this).index()) } )
}
但真正解决问题并慢慢建立起来

第一步 步骤2 简单易懂的替代方案 如果你不能理解这一点,那么这应该更容易理解,并具有相同的效果

for (var i=1; i<=5; i++) {

    function clickHandler(num) {    
        $("#div" + i).click (
            function() { alert (num) }
        )
    }
    clickHandler(i);

}

如果您记得函数变量值是在调用函数时设置的,那么这应该很容易理解,但是这使用了与以前完全相同的思维过程

mm,我以前从未见过使用with语句的解决方案,很好;使用“with”语句时要小心。它有一些性能问题。我读了几个关于这个话题的答案,试图弄清楚为什么。你最后一句话的后半部分终于在我脑海中打开了灯,。。。谢谢,谢谢,谢谢!!我注意到你需要一些更多的代表so+1的直截了当的版本!虽然我认为我个人会把clickHandler函数声明放在循环之外,只是为了风格。这是一个我仍然无法理解的问题。当您说调用函数时设置值,您的意思是仅当单击div时才设置div的每个值?这是节省功能范围的参考所有时间我知道我迟到的党,但无论如何。这叫做闭包。即使外部函数已返回,内部函数也可以访问外部函数中的值。所以外部函数是一个IIFE,所以它存储num值。什么时候
单击,内部函数将执行并返回num。我正在为演示文稿寻找一些关于闭包的好解释。。。到目前为止,您的方法是最好的,值得称赞。糟糕的是,这不再有效,警报根本不会显示,控制台中也不会显示任何内容。请记住,扩展DOM cf.link.i=i;被认为是。@check_ca,但是,同样的事情也可以通过数据属性或类似jQuery的.data来完成。这些通常可以解决文章中的问题,例如,数据是为用户保留的,因此未来的标准永远不会定义数据属性。如果使用DOM节点进行信息存储,它们可以很容易地共享-非常有教育意义,谢谢@检查一下你会推荐什么?至少这种解决方案是有效的,与基于闭包的解决方案不同。@PhilippLudwig我建议用link.setAttributedata链接索引替换link.I=I,并用alertNumberthis.getAttributedata-link-index替换alertthis.I
for (var i=1; i<=5; i++) {
    $("#div" + i).click (
        function(num) {
            // A functions variable values are set WHEN THE FUNCTION IS CALLED!
            // PLEASE UNDERSTAND THIS AND YOU ARE HOME AND DRY (took me 2 years)!
            // Now the click event is expecting a function as a handler so return it
            return function() { alert (num) }
        }(i) // We call the function here, passing in i
    )
}
for (var i=1; i<=5; i++) {

    function clickHandler(num) {    
        $("#div" + i).click (
            function() { alert (num) }
        )
    }
    clickHandler(i);

}