- 闭包:概念:根据词法作用域规定,内部函数总是可以访问其外部函数中声明的变量,当调用一个外部函数返回一个内部函数的时候,即使外部的函数已经执行结束了,但是内部函数引用外部函数的变量依然保存在内存中,我们把这个变量的集合成为闭包。 (这个函数在定义的词法作用域之外调用,闭包使得函数可以继续访问定义的时候的词法作用域。)
- 常见问题:分别输出数字1-5,每秒一次,每次一个。
for(var i=1;i<=5;i++){
setTimeout(()=>{
console.log(i)
},i*1000)
}
上述的代码并不会打印数字1-5,而是连续打印了5个6。这是因为定时器会在循环结束的时候在执行(涉及宏任务,微任务),而i是全局共享的,所以在执行打印的时候就已经是6了。
解决问题的方法:循环中需要有自己的变量,用来在每个迭代中存储i的值。
- 办法1:使用LIFE
for(var i=1;i<=5;i++){
(function(j){
setTimeout(()=>{
console.log(j)
},i*1000)
})(i)
}
- 办法2:在块中声明变量
for(var i=1;i<=5;i++){
let j= i //闭包的块作用域
setTimeout(()=>{
console.log(j)
},j*1000)
}
- 办法3:直接使用let
//for循环头部的let声明有一个特殊的行为,这个行为之处变量在循环过程中布置被声明一次,**每次迭代都会声明**。随后的每次迭代都会使用上一次迭代结束时的值来初始化这变量。
for(let i=1;i<=5;i++){
setTimeout(()=>{
console.log(i)
},i*1000)
}
- 闭包的常见地方(只要使用了回调函数,实际上都是在使用闭包?)
- 定时器
将一个内部函数timer传递给了setTimeout,timer具有涵盖foo的作用域的闭包,保留对变量message的引用。 foo在执行1000毫秒之后,他的内部的作用域并不会消失,timer函数依然保有foo作用域的闭包。
function foo(message){
setTimeout(function timer(){
console.log(message)
},1000)
}
foo('定时器')
- 模块
条件限制 1:必须要有外部的封闭函数,该函数至少要被调用一次(每次调用会创建一个新的模块实例)。2. 封闭函数必须返回至少一个内部函数,这样内部函数才能在私有作用域形成闭包,并且可以直接访问或者修改私有的状态。
function coolMoudlue(){
var name = 'cool'
function something(){
console.log(name)
}
return {
something :something
}
}
var foo = coolMoudlue()//注意,coolMouedle只是一个函数,必须要调用它他创建一个实例。如果不执行外部函数,内部函数和闭包都无法创建。
foo.something()