一篇文章带你了解闭包,闭包,其实也没那么复杂

262 阅读5分钟

一篇文章带你了解闭包,闭包,其实也没那么复杂

闭包的英文名是(Closure)翻译过来就是闭合的意思。闭包是个函数,它不仅能记住自己内部的变量,也能记住自己被创建所在的作用域。即使这个函数被传递到其他地方,它也仍然可以访问原来作用域中的变量。

闭包

概念:一个函数对周围状态的引用捆绑在一起,内层函数中访问到其外层函数的作用域

简单理解:闭包 = 内部函数 + 外部函数的变量

** 示例一:简单的闭包**

function outerFunction(){  // 定义外部函数

let outerVariable="外部函数的变量" // 外部函数的变量

 // 定义内部函数
function innerFunction(){ 

  // 内部函数可以访问外部函数的变量
    console.log(outerVariable); 
}
return innerFunction  // 闭包 外部函数的变量被内部函数引用,形成闭包

}
// 调用外部函数,返回闭包
const myClosure=outerFunction(); 
myClosure()// 外部函数的变量

image.png

在这个例子中:

  • outerFunction 定义了一个外部函数的变量outerVariable 和 一个内部函数 innerFunction

  • 内部函数可以访问外部函数的变量,形成闭包

  • innerFunction 可以访问 outerFunction 定义的变量,形成闭包

  • 即使outerFunction已经执行完毕

  • outerFunction返回了innerFunction,将其赋值给了变量myClosure

  • 当调用myClosure()时,它仍然可以访问outerVariable,形成闭包,这就是闭包的作用

简单来说:

我们可以这样理解,闭包就像是个封闭的小盒子,你在盒子内放了一本日记,这个盒子有个功能:它能记住你写日记时的房间,,即使你把日记给了朋友,他也能看到那本日记

 function noteBookMessage(){
    let Book="这是我的日记"//这是日记的内容
    
    // 这个函数就像是那个盒子,它能记住Book这个日记
   function showMessage(){
     console.log(Book);
    }
    
    // 把盒子(函数)给朋友
       return showMessage;
  }
    // 你把盒子给了朋友
  const myBook=noteBookMessage();
  
    // 朋友打开盒子,看到日记
     myBook();// 这是我的日记

image.png 在这个例子中:

  • noteBookmessage 是一个函数,它创建了一个日记内容Book。

  • showMessage 是一个内部函数,它能访问Book这个外部函数的变量,就像盒子能记住日记里的内容一样。

  • 当你调用noteBookmessage()时,它返回了showMessage函数,这个函数就是一个闭包。

  • 即使noteBookMessage 函数执行完毕,showMessage 函数仍然可以访问Book,因为它记住了创建它时所在的作用域。

示例二:数据封装和私有化

普通形式,统计函数调用的次数

   let i=0
    function fun2(){
        i++
        console.log(`函数被调用了${i}次`);
    }
    fun2()
    fun2()
    i是全局变量,很容易被修改
    以i=100为例

image.png

闭包形式 统计函数调用的次数

     闭包的作用:实现数据的私有化 函数调用,函数调用一次,就++
    let z;
    function count(){
        let i=0
        function fun(){
            i++
            console.log(`函数被调用了${i}次`);
        }
        return fun;
    }
  z= count();//创建闭包
  z() // 1 使用闭包
  z() // 2
  z() // 3

刚才我们举例说到:

闭包就像是一个封闭的盒子,这里面放了你的日记或者你的其他一些秘密的东西。

但盒子上有几个特定的钥匙孔,你只能通过钥匙孔去取放盒子内部的东西,有的钥匙孔可以让你往盒子里放东西,有的可以让你往盒子里拿东西。

通过这种方法,i的值被封装在函数的内部,外部代码无法直接访问它,只能通过fun 方法来操作它。这就体现了数据的封装和私有化,保护了数据的安全性。

示例三:函数工厂

我们知道函数是一段反复调用的代码块,就像是一个模具:你只需要告诉卖家你需要什么样的模具,那你就可以一直生产这种模具所产生的工艺品。

function createMultiplier(multiplier) {

// 这个函数返回一个闭包
  return function(number) {
    return number * multiplier;
  };
}

// 创建一个专门乘以2的函数
const double = createMultiplier(2);

// 创建一个专门乘以3的函数
const triple = createMultiplier(3);

console.log(double(5)); // 输出: 10
console.log(triple(5)); // 输出: 15

说明:

1.工厂函数 (createMultiplier):

  • createMultiplier 是一个工厂函数,它接受一个参数 multiplier, 这个参数决定了返回的内部函数如何处理数字。
  1. 闭包 (返回的函数) :
  • createMultiplier 返回了一个闭包函数。这个闭包函数捕获了 mutiplier参数,即使 createMultiplier 函数执行完毕,返回的函数仍然可以访问multiplier

示例四: 释放闭包

通俗解释: 我们之前说过闭包就像是个封闭小盒子,里面放了我们的日记,我们把盒子关上之后,但我们手里还有这个盒子的钥匙。这把钥匙就是闭包。

如果闭包一直存在,它所引用的变量(盒子里的东西)也就不会被清理掉,即使里面的日记不再使用了。这就导致了内存泄漏。

而避免闭包内存泄漏的方法可以释放闭包:

当你不再需要闭包时,将其设置为null或者其他值,以便垃圾回收站回收内存。

let z;
    function count(){
        let i=0
        function fun(){
            i++
            console.log(`函数被调用了${i}次`);
        }
        return fun;
    }
 z= count();//创建闭包
 z() // 1 使用闭包
 z() // 2
 z() // 3
 z() // 4
 z=null//不需要闭包了,清理它
 z()// 无效
 
 

image.png

闭包的优缺点

优点:

  • 代码整洁 : 闭包可以减少全局变量的使用,使代码更加整洁易于维护。
  • 数据封装 : 闭包可以创建私有变量,防止外部直接访问,从而实现数据封装和私有化。
  • 功能强大 :闭包可以实现很多强大的功能,如函数工厂,延迟计算等

缺点

  • 内存泄漏:如果闭包不再需要被使用,就很可能导致内存泄漏。因为闭包的内部函数会捕捉外部函数的变量,如果这些变量不再需要,但闭包仍存在,就会占用内存。
  • 难以理解:闭包的概念比较抽象,复杂,因为闭包通常涉及至少两个函数,对于初学者来说可能需要一段时间去理解

总结

闭包是javaSript中一个非常强大的特性,它可以让内部函数访问外部函数的作用域,从而实现数据封装,函数工厂等功能。但同时,使用闭包也要防止内存泄漏等问题。希望这篇文章能使你更好的了解闭包!