了解事件处理程序的javascript闭包 [英] Understanding javascript closure with event handlers

查看:69
本文介绍了了解事件处理程序的javascript闭包的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我对javascript很新,最近在了解闭包时我遇到了一个问题,面试官问了一个问题: -

I'm very new to javascript, recently while understanding closures I came across one question asked by Interviewer:-

function initButtons() {
    var body = document.body,
        button, i;

    for (i = 0; i < 5; i++) {
        button = document.createElement("button");
        button.innerHTML = "Button " + i;
        button.addEventListener("click", function (e) {
            alert(i);
        }, false);
        body.appendChild(button);
    }
}
initButtons();

此代码的输出结果是什么?我回答了 - 对应于按钮的数字...... 1,2等。

What will be output of this code? For which I replied - "Number corresponding to button.. 1, 2 etc."

好的,然后我用Google搜索并找到答案,其中说: -

Okay, then I googled around and found a answer, which stated:-


发生这种情况的原因是因为在for循环的每次迭代期间调用addEventListener方法是
时会创建一个闭包。

The reason this happens is because when the addEventListener method is invoked during each iteration of the for loop a closure is created.

好的,现在一切都在我头顶......这怎么可能?坦率地说,我是一个愚蠢的JavaScript,并试图尽可能多地学习。所以,我将通过基础知识!!

Okay, now everything is going above my head... How is this possible? To be frank enough, I'm a dumb in javascript and trying to learn as much possible. So, I am going thru the basics!!

同时我也在阅读 how-do-javascript-closures-work

推荐答案

除此部分外,其中大部分都会按预期方式工作:

Most of this will work the way you expect it to, except this part:

button.addEventListener("click", function (e) {
    alert(i);
}, false);

有人可能期望每个 addEventListener 获得三个参数:

One might expect that each addEventListener gets three parameters:


  1. click

  2. 功能的结果

  3. false

  1. "click"
  2. The result of the function
  3. false

如果是这种情况,五个事件监听器将如下所示:

If that were the case, the five event listeners would look like this:

button.addEventListener("click", alert(0), false);
button.addEventListener("click", alert(1), false);
button.addEventListener("click", alert(2), false);
button.addEventListener("click", alert(3), false);
button.addEventListener("click", alert(4), false);

但是,它不是设置的函数的结果作为第二个参数,它是函数本身。所以实际上,这是你的五个事件监听器:

However, it isn't the result of the function that is set as the second parameter, it is the function itself. So in actuality, here are your five event listeners:

button.addEventListener("click", function(e){alert(i);}, false);
button.addEventListener("click", function(e){alert(i);}, false);
button.addEventListener("click", function(e){alert(i);}, false);
button.addEventListener("click", function(e){alert(i);}, false);
button.addEventListener("click", function(e){alert(i);}, false);

如您所见,所有这些参数的第二个参数是以下函数:

As you can see, the second parameter for all of them is the following function:

function(e){
   alert(i);
}

因此,当您点击其中一个按钮时,将执行上述功能。所以你点击一个按钮然后执行该功能,这是一个有效的问题: i 的价值是多少?背景在这里很重要。上下文不是循环的中间,上下文是你点击了按钮并触发了这个函数。

So when you click on one of these buttons, the above function will execute. So you click on a button and the function executes, here's the operative question: what is the value of i? The context is important here. The context is not the middle of the loop, the context is that you've clicked on the button and fired this function.

聪明猜测是 i 在此上下文中是未定义的。但是当在另一个函数内创建函数时会发生这种棘手的事情:子函数可以访问父函数中的变量。所以仍然可用!那么现在是什么?它是 5 ,因为我们已经将它循环到5,它仍然是5.

A smart guess would be that i is undefined in this context. But there's this tricky thing that happens when a function is created inside another function: the sub-function has access to the variables in the parent function. So i is still available! So what is i right now? It's 5, because we've looped it up to 5, and it remains 5.

所以当你听到人们谈论关闭,他们真正的意思是子功能。子函数很棘手,因为它们以这种方式携带父元素的变量。

So when you hear people talking about closures, all they really mean is "sub-function." Sub-functions are tricky because they carry the variables of their parents with them in this way.

那么如果你想按钮 alert <怎么办? / code>点击按钮对应的数字?如果使用立即执行的函数,则可以执行此操作,因此返回函数的结果,而不是函数本身。为此,您只需将函数表达式包装在parens中,然后使用();

var one = function(){}      // one contains a function.
var two = (function(){})(); // two contains the result of a function

让我们稍微玩一下,看看它会如何影响代码。

Let's play with this a little to see how it would affect the code.

我们可以立即执行事件监听器的第二个参数喜欢这样

We could make the second parameter of the event listener execute immediately like so:

button.addEventListener("click", (function (e) {
    alert(i);
})(), false);

然后你会在加载页面后立即得到5个警报,0-4,点击时什么都没有,因为该函数没有返回任何东西。

Then you would get five alerts, 0-4, immediately upon loading the page, and nothing when clicking, because the function didn't return anything.

这不是我们真正想要的。我们真正想要的是从该循环中触发 addEventListener 。简单易玩 - 只需将其包装在一个立即执行的函数中:

That's not what we really want. What we actually want is to get the addEventListener to fire from within that loop. Easy peasy--just wrap it in a function that executes immediately:

(function(i){
   button.addEventListener("click", function (e) {
     alert(i);
   }, false);
})(i);

请注意,而不是(); 我们现在有(i); ,因为我们将参数 i 传递给它,但除此之外全部都是相同。现在该函数立即被触发,a.k.a。我们将立即获得该函数的结果。这个新的立即执行功能的结果是什么?它实际上会根据循环的迭代次数而改变。以下是循环每次迭代的函数结果:

Note that instead of (); we have (i); now, because we're passing the parameter i to it, but other than that it's all the same. Now the function is fired immediately, a.k.a. we're going to get the result of the function immediately. What is the result of this new immediately executing function? It actually changes depending on which iteration of the loop it is in. Here are the results of the function for each iteration of the loop:

button.addEventListener("click", function (e) {
  alert(0);
}, false);
button.addEventListener("click", function (e) {
  alert(1);
}, false);
button.addEventListener("click", function (e) {
  alert(2);
}, false);
button.addEventListener("click", function (e) {
  alert(3);
}, false);
button.addEventListener("click", function (e) {
  alert(4);
}, false);

所以你现在可以看到点击时触发的五个函数每个都有值在那里硬编码。如果你得到这个,你会得到关闭。就个人而言,我不明白为什么人们在解释问题时会使问题过于复杂。请记住以下内容:

So you can see now that the five functions that will fire on click each have the value if i hardcoded in there. If you get this, you get closures. Personally, I don't understand why people overcomplicate the issue when they explain it. Just remember the following:


  1. 子功能可以访问其父变量。

  2. 这些功能用作表达式(例如分配给变量或其他东西,未定义如函数newFunc(){} )使用作为函数而不是作为功能的结果

  3. 如果你想要函数的结果,可以通过将它包装在(...)();

  1. Sub-functions have access to their parent's variables.
  2. Functions that are used as expressions (e.g. assigned to a variable or something, not defined like function newFunc(){}) are used as a function and not as the result of the function.
  3. If you want the result of the function, you can execute the function immediately by wrapping it in ( ... )();

就我个人而言,我发现这是一种更直观的方式来理解闭包而没有所有疯狂的术语。

Personally I find this a more intuitive way to understand closures without all the crazy terminology.

这篇关于了解事件处理程序的javascript闭包的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆