作用域
- 在理解闭包之前,我首先需要理解js的作用域
- js的作用域中分为两种:全局作用域和局部作用域
- 作用域访问环境中,是由内向外
- 函数内部可以获得当前包含当前作用域的外层作用域下的变量
- 函数外部则无法读取函数内部的局部变量
- 在不同的函数作用域中也是不能相互访问彼此变量的
什么是闭包
- 闭包就解决了上面的问题
- 当我在外部也可以访问到一个函数内部的变量
- 利用函数嵌套函数就可以解决
- 闭包的本质就是在一个函数内部在创建另一个函数
function f1(){
let a = 2
function f2(){
console.log(a)// a=2,f2用到了上面f1的a,所以产生了闭包
}
f2()
}
闭包的用途
- 在内存中维持一个变量,可以做缓存
- 隐藏局部变量,暴露操作函数
let creatAdd = ()=>{
let n = 1
return ()=>{
n+=1
console.log(n)
}
}
let add = creatAdd()
add()//1
add()//2
分析:在一般情况下,函数creatAdd执行完以后,会随即销毁,但是在这里creatAdd的返回值赋值给了add,而且函数内部一直引用着外部n这个变量,所以n这个变量无法销毁,一直维持着
闭包和setTimeout
//没有利用闭包
for(var i = 0; i < 5; i++){
setTimeout(function(){
console.log(i)
},1)
}
//结果打印出5个5
//利用闭包(let声明也可实现)
for(var i = 0; i < 5; i++){
(function(i){
setTimeout(function(){
console.log(i)
},1)
})(i)
}
//分别打印出 0 1 2 3 4
分析:
- 在第一个没有利用闭包的setTimeout,原因是js是单线程操作,在执行for循环时setTimeout定时器被分配到了任务列表当中排队等待执行,当for中的i执行完毕,定时器才开始输出,这时候i已经为5了,所以setTimeout输出的为5个5。
- 第二个是因为利用了闭包来保存了i的值,将setTimeout放入立即执行函数中,将for循环中的循环值i作为参数传递,1毫秒后同时打印出0 1 2 3 4
闭包的缺点
- 被引用的私有变量不能被销毁,增大了内存消耗,造成内存泄漏,解决方法是可以在使用完变量后手动为它赋值为null