Javascript闭包范围

时间:2015-12-02 21:07:48

标签: javascript function javascript-events scope closures

我今天在一个前端Javascript项目上工作。我会尽量保持问题和解决方案的描述尽可能短。 我不得不将点击处理程序添加到将用户重定向到其他页面的页面上的链接,所以我有2个Javascript数组arrayOfRedirectLinkspageLinkElements

var arrayOfRedirectLinks, pageLinkElements;

最初我写了这样的addEventHandlers函数:

var addEventHandlers = function() {
    var i, link;
    for( var i in arrayOfRedirectLinks) {
        link = arrayOfRedirectLinks[i];
        pageLinkElements[i].addEventListener('click', function(e) {
            e.preventDefault();
            window.location = link;
        });
    }
}

我认为这个解决方案可以完成这项任务直到......直到我打开浏览器,点击几个链接并注意到所有链接都将我重定向到同一个链接(arrayOfRedirectLinks中的最后一个链接)

最后我发现我的问题类似于此处发布的问题 Javascript multiple dynamic addEventListener created in for loop - passing parameters not working

事实上,那里发布的第一个和第二个解决方案都适用于我

var addEventHandlers = function() {
    var i, link;
    for( var i in arrayOfRedirectLinks) {
        (function(link){
        link = arrayOfRedirectLinks[i];
        pageLinkElements[i].addEventListener('click', function(e) {
            e.preventDefault();
            window.location = link;
        });
        }(link));
    }
}

var passLink = function(link) {
    return  function(e) {
               e.preventDefault();
               window.location = link;
            };
};
var addEventHandlers = function() {
    var i, link;
    for( var i in arrayOfRedirectLinks) {
        link = arrayOfRedirectLinks[i];
        pageLinkElements[i].addEventListener('click',passLink(link));
    }
}

现在这似乎有效但我不明白为什么它有效。 我带来了以下解释,如果有人能确认这是否正确,我想要:

1)当我在Javascript中声明一个函数时,它获取对声明它的函数范围内的变量的引用。 (即我的事件处理程序获得对link函数中的addEventHandlers变量的引用)

2)因为处理程序获得对变量link的引用。当我将值重新分配给link变量时,触发点击处理程序时将使用的值也将更改。因此,事件处理程序中的link变量不是简单地使用与添加函数处理程序时不同的内存地址和相同值的link进行复制,但它们都共享相同的内存地址,因此相同的价值。

3)由于2)中描述的原因,所有点击处理程序将使用重定向到同一link,即数组link中的最后arrayOfRedirectLinks,因为这是将在link循环结束时分配给for变量的最后一个值。

4)但是当我将link变量作为参数传递给另一个函数时,它创建了一个新的范围,并且该范围内的link实际上只与{的值一起初始值{传递给函数的{1}}参数。 2 link变量的引用是不同的。

5)由于4),当我将link传递给click处理程序时,它将引用立即调用函数表达式中的link变量,该变量本身不共享与link函数中的link相同的地址。因此,事件处理函数中的每个addEventHandlers都将与其他函数隔离,并保留link

的值

这是对的吗?

1 个答案:

答案 0 :(得分:3)

这是关键点:

var i, link;
for( var i in arrayOfRedirectLinks) {
  link = arrayOfRedirectLinks[i];
  pageLinkElements[i].addEventListener('click', function(e) {
    e.preventDefault();
    window.location = link;
  });
}

这里只有一个link变量。请注意,仅在单击链接时才会调用addEventListener回调函数。

到那时,变量link有它的最终值,由所有事件处理函数共享。

所以所有链接都做同样的事情。

最简单的解决方案(除了更广泛的重构):

for(var i in arrayOfRedirectLinks) {      
  (function(i) {
    var link = arrayOfRedirectLinks[i];
    pageLinkElements[i].addEventListener('click', function(e) {
      e.preventDefault();
      window.location = link;
    });
  }(i));
}