当函数记住并访问所在的词法作用域,即使函数是在当前词法作用域之外执行,这时就产生了闭包。达成自己私有作用域的函数体就是闭包。

概念

MDN:那些能够访问自由变量的函数 JavaScript高级程序设计:有权访问另一个函数作用域中的变量的函数 JavaScript权威指南:从技术的角度,所有的JavaScript函数都是闭包

从理论上讲:所有的函数都是闭包,因为闭包的定义是指那些能访问自由变量的函数,而自由变量是指在函数中使用,但既不是函数参数,也不是函数内部声明的局部变量。

从实际上讲:即使创建它的上下文已经被销毁,它依然存在(比如内部函数从父级函数中返回),在代码中引用了自由变量。

函数的执行和定义不在同一个作用域,它就是闭包。

使用

闭包是在函数被调用的时候才会被确定创建的,闭包的形成与作用域链的访问顺序有直接的关系,只有内部函数访问上层作用域链中的变量对象才会形成闭包。

通过作用域链来看一下:

  1. var scope = "global scope";
  2. function checkscope(){
  3. var scope = "local scope";
  4. function f(){
  5. return scope;
  6. }
  7. return f();
  8. }
  9. checkscope();
  10. // fContext的作用域链
  11. ScopeChain = [AO, checkscopeContext.AO,globalContext.VO]

当checkscopeContext已经被回收了,但是fContext依然引用这checkscopeContext.AO,保留着checkscopeContext中的变量,并进行使用,这就形成了闭包。

好处

  1. 立即执行函数
  2. 类库封装,隔离作用域,避免变量污染
  3. 实现类和继承

弊端

  1. 内存泄露
  2. this指向问题
  3. 引用的外部变量改变不会在内部不会生效
  4. for循环形成闭包(函数工厂、匿名闭包、let)

注:造成内存泄露的原因:

  1. 全局变量
  2. 闭包
  3. dom删除或者清空时绑定的事件未清除