Skip to content
On this page

闭包

是什么

在 JavaScript 中,函数可以作为参数和返回值, 当一个内嵌函数的引用环境发生了改变,但仍需要访问原来的引用环境, 就必须以某种方式将内嵌函数的引用环境和它捆绑在一起,这个整体就称为函数的闭包(closure)。

很多文章是这样介绍闭包的:从某个函数返回的函数所记住的上下文信息。

但实际上闭包不只是在函数被返回时才创建的。

不返回函数,但是有闭包的效果

function f(fn, x) {
  if (x < 1) {
    f(g, 1);
  } else {
    fn();
  }
  function g() {
    console.log(x);
  }
}
function h() {}
f(h, 0); // 输出结果 -> 0 而不是 1

什么是内嵌函数

function test() {
  const a = 1;
  return function closure() {
    console.log(a);
  };
}

在这种情况下,我们将函数 closure 称为内嵌函数,而 test 称为外套函数。

经典例子

var list = document.createElement('ul');
for (var i = 1; i <= 5; i++) {
  var item = document.createElement('li');
  item.appendChild(document.createTextNode('Item ' + i));
  item.onclick = function (e) {
    console.log('Item ' + i + ' is clicked.');
  };
  list.appendChild(item);
}
document.body.appendChild(list);

这段代码的原意是想单击每个li元素时,打印它们的编号,但实际上,无论单击哪一个li,打印输出的都是Item 6 is clicked.

使用闭包

item.onclick = (function (j) {
  return function (e) {
    console.log('Item ' + j + ' is clicked.');
  };
})(i);

使用 let

for (let i = 1; i <= 5; i++) {
  // 省略
}

题外话

类似于这种需求,我们可以使用事件委托的机制做一个优化,仅仅给父元素添加一个事件监听器,而不是给每个子元素添加事件监听器。

参考资料

  • 《JavaScript 函数式编程思想》潘俊