闭包

302 阅读2分钟

作用域

  • 在理解闭包之前,我首先需要理解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