闭包

闭包

什么是闭包

其实一直以来闭包根本没有一个确定的定义,即使有所谓的定义,你也看不明白,网上很多人说的定义,也是各不相同,但唯一相同的是,大家都会说怎样做能产生一个闭包。

闭包其实是一个函数的实例,该实例是一个存在于内存中的某个结构体。也就是说闭包不是静态的代码,它是在函数运行时产生的一个函数及其运行时创建的环境的综合体。

那么这个函数实例(即这个结构体)里有什么呢?第一有这个函数(的入口地址)以及一个关联的环境(其实就是一些变量的键值对),这些变量里有自由变量(函数外部的变量,但被在函数内引用了),也有约束变量(函数内部的变量)。

我们知道,对象是类的实例,对象的实质也是方法与属性的组合(这与闭包是函数及其词法环境的组合非常相似),那闭包和对象有什么关系吗?其实我个人认为,闭包就是一个简单的对象,在函数式编程中可以用闭包来实现类似面积对象的一些功能,比如静态私有变量。

闭包产生的条件: 函数A返回一个函数B(即把函数当返回值,函数B经常是匿名函数),且函数B里调用了函数A内的局部变量(一定得是局部变量),则在函数A外部执行函数B时就会产生一个闭包(Closure)。

闭合示例代码

说到闭包,它最常见于js中

//定义一个函数A
function A (){
    //函数A内部的局部变量sum
    let sum = 0;
    //在函数A里定义一个函数B,且在函数B里引用了外部变量(即函数A中的局部变量)sum
    let B = function (num){
        sum += num;
        return sum;
    }
    //最终返回函数B(注意是返回的函数B本身,而不是对函数B的调用)
    return B;
}

//执行函数A,得到的f其实是函数B
let f = A();

//输出:1
console.log(f(1));
//输出:3
console.log(f(2));
//输出:6
console.log(f(3));

用php实现闭包

<?php
    //定义一个函数A
    function A(){
        //函数A的内部的局部变量sum
        sum = 0;
        //php实现闭包的两个注意点:
        //1.函数要直接引用外部变量,要用use()方法,即需要显式引用
        //2.变量必须加&符,这样表示使用它的引用而不是拷贝B = function (num) use(&sum){
            //在函数内部引用了外部(即函数A的局部)变量
            sum +=num;
            return sum;
        };
        //返回函数B(注意是返回函数本身而不是对函数的调用)
        returnB;
    }
    //调用函数A(),返回的其实是函数B,所以f其实就
    //是函数B,加个括号就可以执行它(当然要传参数)f = A();

    //输出:1
    echo f(1)."\n";
    //输出:3
    echof(2)."\n";
    //输出:6
    echo $f(3)."\n";

使用python实现闭包

#定义一个函数A
def A():
    #函数A内部的局部变量sum
    sum = 0
    #在函数A里定义一个函数B,且在函数B里引用了外部变量(即函数A中的局部变量)sum
    #注意python没有真正意义的匿名函数,所以这里必须用def定义函数
    def B(num):
        #注意python必须用nonlocal关键字描述sum,否则它无法使用它外部的变量,注意这并不是说
        #把sum变成了全局变量了,变成全局变量是用global,nonlocal只是表示引用函数外部的变量
        nonlocal sum
        sum += num
        return sum
    #最终返回函数B(注意返回的是函数B本身,而不是对函数B的调用)
    return B

#执行函数A,得到的f其实是函数B
f = A()

#证明一下,这里打印sum,是打印不出函数A()内部的变量sum的
#即nonlocal只是表示使用函数外部的变量,而没有把sum变成全局变量
print(sum)

#输出:1
print(f(1))
#输出:3
print(f(2))
#输出:6
print(f(3))

在golang中实现闭包

package main

import "fmt"

//注意“func(int) int”这个整体,才是函数A的返回类型(即这
//个返回类型已经不是一个单独的类型,而是一个整体的函数类型)
func A() func(int) int {
    //函数A的内部的局部变量sum
    sum := 0

    //在函数A里定义一个函数B(可以是匿名函数),且函数B里使用了它外部的变量(该变量
    //属于包含它的函数,即函数A的局部变量),当然函数B也可以有自己的局部变量
    B := func(num int) int {
        sum += num
        return sum
    }
    //在函数A里返回函数B(注意是函数B本身而不是对函数B的调用)
    return B
}

func main() {
   //调用函数A(),注意它返回的是函数B,所以现在f就相当于函数B
   //(即f加括号是可以执行的,当然要传参数,因为函数B要求要参数)
   f := A()

   //输出:1
   fmt.Println(f(1))
   //输出:3
   fmt.Println(f(2))
   //输出:6
   fmt.Println(f(3))
}

以上四个代码其实都是一模一样的,只不过是用不同的语言去实现了。另外三个示例代码中的函数B其实可以直接return,即return一个匿名函数,而不需要特地定义一个函数B,我只是为了示例好理解。

通过三个输出我们可以知道,变量sum并没有在第一次调用完f(1)后被销毁,而是保留在内存中(这就是为什么说闭包是函数与关联环境的组合),在后面的调用中,会使用该变量原来的值累加,而不是从0开始。

我们知道,用静态变量也可以实现这个效果,那么通过闭包来实现有什么不一样呢?当然有,不一样的地方是,静态变量是全局的,而通过闭包实现的静态变量却不是全局的,在外部无法改变,只能通过一个暴露出来的接口(即函数B)来对它进行改变,所以通过闭包可以实现静态私有变量,而且这也是一种在函数外部访问函数内部局部变量的方法。

闭包的作用

实现静态私有变量,减少全局变量,防止变量名污染

打赏

Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

0 Comments
Inline Feedbacks
View all comments
0
Would love your thoughts, please comment.x
()
x

扫码在手机查看
iPhone请用自带相机扫
安卓用UC/QQ浏览器扫

闭包