1.如何产生闭包(条件)?闭包的三大要素是什么?

①函数存在嵌套关系
②内部函数必须引用外部函数变量对象上的局部变量
③外部函数必须执行

2.闭包到底是什么?

理解一:闭包是嵌套的内部函数(绝大部分人);
理解二:包含被引用变量(外部函数)的对象(极少数人);
**理解三:所谓的闭包是一个引用关系,该引用关系存在于内部函数中,引用的是外部函数的变量的对象(深入理解).**

3.常见的闭包

①将函数作为另一个函数的返回值(示例如下)

 `function F1() {
     var a = 100
      return function () {
          console.log(a)
      }
  }
  var f1 = F1()
  var a = 200
  f1()  //100`

②将函数作为实参传递给另一个函数调用(示例如下)

  `function F1() {
      var a = 100
      return function () {
          console.log(a)
      }
  }
  function F2(f1) {
      var a = 200
      console.log(f1())
  }
  var f1 = F1()
  F2(f1)  //100`

③使用闭包实现私有方法操作独立的私有属性(可以理解为只支持被所处同一个类下的其他方法所调用)

`function counter() {
    var num = 0;

    function changeValBy(val) {
        num += val;
    }

    return {
        increment:function(){
            changeValBy(1);
        },
        decrement:function(){
            changeValBy(-1);
        },
        viewValue:function(){
            console.log(num);
        }
    }
}`

4.闭包的作用(好处)

①延长外部函数变量对象的生命周期
②让函数外部可以操作(读写)到内部函数的数据(变量/函数)
③注意: 浏览器为了性能后期将外部函数中不被内部函数使用的变量清除了

5.闭包的生命周期

①在嵌套内部函数定义完时就产生了(不是在调用),外部函数调用的时候
②死亡:在嵌套的内部函数成为垃圾对象时

6.自定义模块(模块化)

具有特定功能的js文件
将所有的数据和功能都封装在一个函数内部(私有的)
只向外暴露一个包含n个方法的对象或函数
模块的使用者,只需要通过模块暴露的对象调用方法来实现对应的功能
使用自调用和不使用自调用函数区别
自调用(外层套匿名函数自调用,内部用window.xxx把能够暴露的属性直接添加给window,方便外部使用)
不使用自调用(普通函数,接口可以暴露一个对象return,对象内部包含能够暴露的数据,外部通过调用,比如var a = xxx( ).然后通过
a.xxx( ) 来使用)

7.闭包的缺点和解决(内存泄漏和内存溢出)

内存泄漏:内存无法释放;
内存溢出:内存被撑爆;
f = null ;解决方式 ;让闭包机制清除,必须删除外部函数调用的时候生成的(定义的那个对应内部函数);

8,经典案例(循环中创建闭包)

代码如下:
`for(var i=0;i<10;i++){
     setTimeout(function(){
         console.log(i);
    },100);
 }`
出现的问题:
1.为什么十次打印值都一样?
答:正如闭包的第二点应用一样,这十个打印函数都处于同一作用域链下,是共享同一环境的闭包,它们访问是同一个i值,所以打印出来的值都一样。

2. 为什么打印出来的值是10?
答:这与setTimeout的运行机制有关系,setTimeout是一个异步函数,当他被调用后,是不会立即执行的,而是添加到当前任务队列后面,直到之前的任务都执行          
    完毕后才会执行。
执行的过程:
for循环每执行一次,就将一个setTimeout任务添加到队尾,直到for循环执行完毕,才会开始执行十次setTimeout任务,然而此时的i已经变成了10,所以就会打    
印出十次10啦!
解决方法:
1.利用闭包应用的第三点,让每个打印函数成为独立的闭包,并利用闭包保存当前循环的i值:(类似于一个匿名函数)
  `for(var i = 0; i<10;i++) {
      (function func(j) {
          setTimeout(function() {
              console.log(j);},100)
      })(i);
  }`
2.因为过多的闭包会占用更多的内存,所以为了避免在不必要的时候使用闭包,我们可以采用let关键字:
`for(let i=0;i<10;i++){
        setTimeout(function(){
             console.log(i);
         },100);
     }`