# 闭包

# 闭包是啥

MDN中这么定义闭包: 闭包是 函数声明该函数的词法环境 的集合

// 例子:函数工厂
function makeAdder(x) {
  return function(y) {
    return x + y;
  };
}

var add5 = makeAdder(5);
var add10 = makeAdder(10);

console.log(add5(2));  // 7
console.log(add10(2)); // 12
1
2
3
4
5
6
7
8
9
10
11
12

# 闭包和函数作用域的联系

函数作用域是产生闭包的原因

# 为什么要用闭包

闭包允许将函数与其所操作的某些数据(环境)关联起来。

这显然类似于面向对象编程。面向对象编程中,对象允许我们将某些数据与一个或多个方法相关联

用闭包模拟私有方法(数据隐藏和封装 )

var makeCounter = function() {
  var privateCounter = 0;
  function changeBy(val) {
    privateCounter += val;
  }
  return {
    increment: function() {
      changeBy(1);
    },
    decrement: function() {
      changeBy(-1);
    },
    value: function() {
      return privateCounter;
    }
  }
};

var Counter1 = makeCounter();
var Counter2 = makeCounter();
console.log(Counter1.value()); /* logs 0 */
Counter1.increment();
Counter1.increment();
console.log(Counter1.value()); /* logs 2 */
Counter1.decrement();
console.log(Counter1.value()); /* logs 1 */
console.log(Counter2.value()); /* logs 0 */
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27

计数器counter1和counter2是相互独立的,每个闭包都是引用自己词法作用域内的变量privateCounter,在一个闭包中对变量的修改不会影响到另一个闭包中的变量

# 怎么使用闭包

闭包中常见的问题: for click 问题

const lists = document.querySelectorAll('li')
for(var i = 0; i < lists.length; i++) {
  lists[i].onclick = function () {
    alert(i)
  }
}
1
2
3
4
5
6

问题: 上面的代码,每次点击的时候都是alert(3)

原因: 作用域问题,在点击的时候再去取i的值,已经是lists.length了,使用let创建i,不会出现这个问题

解决方法: 使用闭包 或者 使用let创建i

// 闭包解决
const lists = document.querySelectorAll('li')
for(var i = 0; i < lists.length; i++) {
  (function() {
    lists[i].onclick = function() {
      alert(i)
    }
  })(i)
}
1
2
3
4
5
6
7
8
9

原因: 在上面的情况中,i是全局变量,在onclick时间触发的时候,访问的是全局变量i,所以i一直都是3,如果放在闭包中执行,实参传递到形参的时候,非引用类型的实参会复制一份给形参,而不是共享一份