一、什么是闭包
函数可以记住并访问所在词法作用域时,就产生了闭包,即使在词法作用域外调用函数。
(也就是说如果一个函数在执行完之后,其中的内部包含的函数仍然对该函数的作用域持有着引用(函数执行完之后,内存没有被释放,
因为还一直被引用着),那么这个引用就是一个闭包)
二、辨别闭包
1、也就是这样的一个函数也不能算做真正的闭包,对变量a的查找其实只不过是遵循词法作用域找到的。
function foo1() { var a = 3; function foo2() { console.log(a) }
foo2() } foo1()
执行函数foo1,将foo1内部的函数引用返回给全局变量foo,foo1执行完,内存没有被回收,因为foo一直引用着foo1中的foo2函数,foo1内存不会被释放。通过foo可以随时访问foo1中的变量。
var a = 4
function foo1() { var a = 3 function foo2() { console.log(a) } return foo2 }
var a = 5
var foo = foo1()
foo() //3
无论通过何种方式将内部函数的引用传递到所在词法作用域以外再去调用。
var a = 4 function foo1() { var a = 3 function foo2() { console.log(a) } bar(foo2) } function bar(fn) { var a = 6 fn() } foo1()
2、定时器、事件监听器、Ajax请求、跨窗口通信、web works或任何其他的异步任务中,只要用了回调函数(回调函数执行实质是在当前词法作用域以外的作用域调用的,这也就是为什么回调函数中的this不是指向代码书写的词法作用域的原因),就是在使用闭包。
三、闭包的使用
1、典型案例
每隔1秒打印一个11,创建了10个定时器,每个定时器里面都引用同一个i
for(var i = 1; i<11; i++) { setTimeout(function(){ console.log(i) }, 1000*i) }
循环打印数字,打印1到10的数,每隔一秒打印1个数:
for(var i = 1; i<11; i++) { (function(i) { setTimeout(function(){ console.log(i) }, 1000*i) })(i) }
for(let i = 1; i<11; i++) { setTimeout(function(){ console.log(i) }, 1000*i) }
2、创建模块