在事件处理程序中关闭

Closure in event handler

var element = document.getElementById('test-button');

//event handler
element.onclick = (function outer() {

  var counter = 0;

  return function inner() {

    counter++;
    alert('Number of clicks: ' + counter);
  };
})();
<button id="test-button">Test</button>

我不明白为什么变量计数器不会在每次点击按钮时都重置为0,或者当它被点击时,只执行函数inner(),那为什么当第一次点击发生时,变量计数器设置为 0。 代码来自这个link,仔细看了他们的解释,还是一头雾水

why the variable counter get set to 0 when first click occurs.

那不是它被设置为 0 的时刻。当分配给 .onclick 时会发生这种情况。在那一刻 outer 被定义 并且 被执行。这就是 counter 设置为 0 的时刻。尚未处理任何点击。

only the function inner() get executed

确实如此。 outer 只执行一次,当主脚本在页面加载时执行,然后再也不会执行。 inner 是分配给 onclick 的函数,因此具有事件处理程序的作用。 inner 可以访问 outer 中定义的 counter,但是 outer 再也不会执行了。

outer就是所谓的立即执行函数。

作为比较:您可以使用以下代码实现类似的功能:

var counter = 0;
element.onclick = function inner() {
    counter++;
    alert('Number of clicks: ' + counter);
};

...但现在的缺点是 counter 是一个全局变量,可能会被其他代码更改。为了确保 counter 可用于点击处理程序,为其创建了一个闭包,您也可以这样做:

function outer() {
    var counter = 0;
    element.onclick = function inner() {
        counter++;
        alert('Number of clicks: ' + counter);
    };
}
outer();

这里的缺点是 outer 有一个所谓的 副作用 :它分配给 element.onclick。这就是选择 return inner 并将其留给 outer 的调用者对其进行处理的原因,例如将其分配给 .onclick.

然后我们得到这样的东西:

function outer() {
    var counter = 0;

    return function inner() {
        counter++;
        alert('Number of clicks: ' + counter);
    };
}

element.onclick = outer();

这和你的代码本质上是一样的:先定义函数,然后立即执行。分配给 onclick 的函数是 return 由 outer 编辑的函数,即 inner.

另一种实现 counter 封装的方法:

function inner() {
    this.counter++;
    alert('Number of clicks: ' + this.counter);
}

element.onclick = inner.bind({ counter: 0 });

这里我们提供了一个未命名的对象,无论何时调用它,它总是作为 this 参数传递给 inner。由于我们不在此构造之外保留对该对象的引用,因此我们实现了相同的原则:保持计数器的状态,但在处理程序之外无法访问。