当函数记住并访问所在的词法作用域,即使函数是在当前词法作用域之外执行,这时就产生了闭包。达成自己私有作用域的函数体就是闭包。
概念
MDN:那些能够访问自由变量的函数 JavaScript高级程序设计:有权访问另一个函数作用域中的变量的函数 JavaScript权威指南:从技术的角度,所有的JavaScript函数都是闭包
从理论上讲:所有的函数都是闭包,因为闭包的定义是指那些能访问自由变量的函数,而自由变量是指在函数中使用,但既不是函数参数,也不是函数内部声明的局部变量。
从实际上讲:即使创建它的上下文已经被销毁,它依然存在(比如内部函数从父级函数中返回),在代码中引用了自由变量。
函数的执行和定义不在同一个作用域,它就是闭包。
使用
闭包是在函数被调用的时候才会被确定创建的,闭包的形成与作用域链的访问顺序有直接的关系,只有内部函数访问上层作用域链中的变量对象才会形成闭包。
通过作用域链来看一下:
var scope = "global scope";
function checkscope(){
var scope = "local scope";
function f(){
return scope;
}
return f();
}
checkscope();
// fContext的作用域链
ScopeChain = [AO, checkscopeContext.AO,globalContext.VO]
当checkscopeContext已经被回收了,但是fContext依然引用这checkscopeContext.AO,保留着checkscopeContext中的变量,并进行使用,这就形成了闭包。
好处
- 立即执行函数
- 类库封装,隔离作用域,避免变量污染
- 实现类和继承
弊端
- 内存泄露
- this指向问题
- 引用的外部变量改变不会在内部不会生效
- for循环形成闭包(函数工厂、匿名闭包、let)
注:造成内存泄露的原因:
- 全局变量
- 闭包
- dom删除或者清空时绑定的事件未清除