谈谈闭包

103 阅读3分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第29天,点击查看活动详情

一、前言

闭包是JavaScript的显著特征,在各种JavaScript书籍里面都有详细介绍,也是面试高频考点,可以说闭包贯穿看了整个JavaScript知识,因为它涉及到作用域、垃圾回收、执行上下文等。看了很多文章,每个人说的都不太一样,今天就来谈谈我对闭包的理解。

二、理解闭包

先说说闭包的一些原理知识

1. 如何产生

  • 函数嵌套函数
  • 内部函数引用外部函数的变量
  • 外部函数执行

2. 闭包作用

  • 延长了局部变量的生命周期
  • 在全局中可以间接的操作局部变量

3. 闭包的声明周期

  • 出生:外部函数调用,内部函数声明提升后定义完成就出现了闭包
  • 死亡:当内部函数没有被引用的时候,变成垃圾对象的时候,闭包消失

4. 闭包的缺点

  • 内部的变量长期占用内存空间,可能会导致内存泄漏

5. 如何解决缺点

  • 减少使用闭包
  • 手动释放闭包,将内部函数的引用取消掉

6. 简单写个闭包

   function fn1(){
      let a = 1
     function fn2(){
       a++
       console.log(a)
     }
      return fn2
   } 
   var res = fn1()
   res() //2
   res() //3
   res() //4
   console.log(res) //fn2(){ a++ console.log(a)}
   res = null //手动释放
   console.log(res) //null

三、使用闭包

通过上述一些基本知识的了解,也大算是对闭包有了一个基本概念,下面继续看看闭包在代码中如何应用。

1. 封装私有变量

因为JavaScript特性的问题,不能像Java等其它语言一样去直接写一个私有变量,有很多场景,我们不希望有的代码细节暴露在外部,只是希望在函数内部,就需要用到闭包的形式去封装这些私有变量

  1. 代码呈现
   function Fn(){
     //在外部函数定义一个计数器
     var num = 0
     console.log(num)
     //在内部创建一个用于访问计数器的方法
     this.getNum = function(){
       return num
     }
     //在内部创建一个用于累加计数器的方法
     this.addNum = function(){
       num++
       console.log(num)
     }
   }
   
   //创建实例调用
   var res = new Fn()
   res.addNum() //1
   res.getNum() //1
   console.log(res.num) //undefined
  1. 小结 从上数代码就能看出闭包对私有变量的封装,当我们进行实例调用的时候res.addNum()可以直接内部函数访问外部函数变量,res.getNum()也是同理。但是res.num外部作用域无法获取num的值。

2.回调函数

用闭包处理回调函数其实很常见,回调函数说的是定义了函数,定义了参数,函数内部也实现了,但是不确定在某一时刻异步调用的函数,因此回调函数需要访问到外部的数据。

  1. 代码示例
   function fn(num){
     var s = 1
     var nums = num+1
     //计时器传入一个回调函数
     var timer = setInterval(function(){
       if(s<100){
          nums++
          s++
       }else{
         //100次之后停止定时器
         clearInterval(timer)
         console.log(nums) //100
         console.log(s) //100
       }
     },20)
   }
   fn(0)
  1. 小结 从代码的整个执行过程来看,逻辑执行代码都在函数内部,只需要传入参数即可,全程有s、nums、timer个变量,一个是外部传入内部进行进一步赋值,一个是函数内部,另一个是定时器。这样既可以达到效果,又可以避免变量定义在全局造成污染。而且这些变量也是属于私有变量,相当于每次调用都是独立的,如果没有闭包这一切都会变得很麻烦。

四、总结

  • 通过创建闭包,可以访问闭包内所有的变量。同时闭包还为函数创建提供了安全的作用域,即使创建函数时所在的作用域已经消失,但是函数依然能获得执行时所需要的内容
  • 通过闭包我们可以实现私有变量和函数的封装
  • 保护了函数内部的安全
  • 但是如果大量使用闭包,如果没有手动释放的话,还是会造成内存的消耗

好了,以上就是本篇文章的分享,感谢阅读!