• js 一分钟学会使用闭包


    前言

    今天来谈谈闭包,不从晦涩难懂的文字来谈,直接用简单的代码,了解闭包到底大概是什么样子的,我们平时又怎么用到它。

    函数作用域

    了解闭包,我们首先要知道函数的作用域,它有一个特点,就是与调用时所在的作用域无关,只跟定义的所在的作用域有关,也就是从函数被写好时就固定了。

    严格来讲,所有函数都是闭包。

    但是因为我们平时写代码最多的状态,函数定义所在的作用域和函数调用时所在的作用域是一个作用域,所以闭包没什么讨论的意义。

    下面是非常简单的例子,我们很容易得出,结果是out

    let n = 'out'
    // 函数定义时
    let outFunc = () => {
        console.log(n)
    }
    // 函数调用时
    outFunc() // out
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    假如我们嵌套了一个函数在内部,下面例子我们可以得到什么结论呢?

    虽然我们的函数是在外部调用的,但是我们函数中输出结果变量n是内部函数定义时所在的变量作用域中的n

    这就是闭包值得讨论的东西。

    let n = 'out'
    let outFunc = () => {
        let n = 'in'
        // 函数定义时
        return () => {
            console.log(n)
        }
    }
    // 函数调用时
    outFunc()() // in
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    私有变量

    我们知道了闭包的价值存在于函数中嵌套着另一个函数时,内部的函数可以访问外部函数内的局部变量,它有什么用呢?


    我们先来看看这个例子,这个例子实现了函数执行对于某个变量的计数自增。

    let count = 0
    let add = () => {
        console.log(count++)
    }
    
    add() // 0
    add() // 1
    add() // 2
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    这段代码存在一个问题,就是count太容易被人修改了,这样的自增并不安全。


    我们如何让count不会被人意外修改呢?让它成为私有变量。

    我们就可以借用闭包的思想。

    let add = (() => {
        let count = 0
        return () => {
            console.log(count++)
        }
    })()
    
    add() // 0
    add() // 1
    add() // 2
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    当外部的函数add在外部被定义出来的那一刻,内部函数所使用的局部变量count已经处于只有它可以访问的状态,属于它的私有变量


    但是我们发现我们上面写的这种私有状态没办法复用,因为我们的add定义时就被调用,内部的私有状态只被一个闭包独享。

    我们可以使用一个外部函数定义多个闭包,达到复用私有状态的目的:

    let add = () => {
        let count = 0
        return () => {
            console.log(count++)
        }
    }
    
    let add1 = add()
    let add2 = add()
    
    add1() // 0
    add1() // 1
    add2() // 0
    add2() // 1
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    还有局部的私有变量我们可以提升到入参位置,达到设置初始值的目的,除了定义时,后续也是不可修改的。

    let add = (count) => {
        return () => {
            console.log(count++)
        }
    }
    
    let add1 = add(0)
    let add2 = add(100)
    
    add1() // 0
    add1() // 1
    add2() // 100
    add2() // 101
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    综上,我们可以简单的做个总结,一个内部的函数被定义在一个外部的函数中,就可以将外部函数的变量作为自己的私有变量。

    继续用一个例子加深理解,只要是内部的函数被定义时就可以了,即便这个函数是某个对象上的方法也可以。

    let obj = {}
    
    let addPrivateCount = (obj) => {
        let count = 0
        obj.getCount = () => {
            console.log(count++)
        }
    }
    
    addPrivateCount(obj)
    
    obj.getCount() // 0
    obj.getCount() // 1
    obj.getCount() // 2
    
    console.log(obj) // { getCount: [Function (anonymous)] }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    我这里顺便输出了obj,告诉你上面只存在一个获取count的方法,但是并没有count变量,count可以算作它私有的变量,虽然可以获取,但是除了它的getCount方法访问,没有别的办法访问。

    尾言

    如果能够读懂这些小例子,相信闭包已经难不到你了,而且你也能够利用闭包去解决实际编程中的一些问题。

    如果本文对你有帮助的话,欢迎点赞收藏,有什么意见或问题也欢迎提出,感谢~

  • 相关阅读:
    Android AMS——进程LRU列表更新(十七)
    leetcode Top100(17)矩阵置零
    oracle报错,XML节点值超过varchar(4000)截取方法。ORA-06502,ORA-06512,ORA-01706
    工作中何如来合理分配核心线程数?
    CSS选择器
    BGP选路属性之权重
    perl 用 XML::Parser 解析 XML文件,访问哈希
    【开源】基于JAVA的校园二手交易系统
    系统性详解Redis操作Hash类型数据(带源码分析及测试结果)
    差分信号变送器模块使用说明
  • 原文地址:https://blog.csdn.net/weixin_43877799/article/details/126704756