闭包

闭包

什么是闭包

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

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

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

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

闭包产生的条件: 函数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(注意是返回函数本身而不是对函数的调用)
        return $B;
    }
    //调用函数A(),返回的其实是函数B,所以$f其实就
    //是函数B,加个括号就可以执行它(当然要传参数)
    $f = A();

    //输出:1
    echo $f(1)."\n";
    //输出:3
    echo $f(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开始。

闭包的作用

  • 1、对js,和golang来说,函数外部的全局变量都可以直接在函数内被调用,它们没有静态局部变量,所以用闭包可实现静态私有变量,减少全局变量,防止变量名污染;
  • 2、对php来说,主要不是用在模拟静态私有变量上,因为php本身就有静态变量,可以用静态变量达到闭包实现的静态私有变量的效果,其它用处我还没有研究,暂不清楚;
打赏

订阅评论
提醒
guest

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据

0 评论
内联反馈
查看所有评论
0
希望看到您的想法,请您发表评论x

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

闭包