闭包
什么是闭包
其实一直以来闭包根本没有一个确定的定义,即使有所谓的定义,你也看不明白,网上很多人说的定义,也是各不相同,但唯一相同的是,大家都会说怎样做能产生一个闭包。
闭包其实是一个函数的实例,该实例是一个存在于内存中的某个结构体。也就是说闭包不是静态的代码,它是在函数运行时产生的一个函数及其运行时创建的环境的综合体。
那么这个函数实例(即这个结构体)里有什么呢?第一有这个函数(的入口地址)以及一个关联的环境(其实就是一些变量的键值对),这些变量里有自由变量(函数外部的变量,但被在函数内引用了),也有约束变量(函数内部的变量)。
我们知道,对象是类的实例,对象的实质也是方法与属性的组合(这与闭包是函数及其词法环境的组合非常相似),那闭包和对象有什么关系吗?其实我个人认为,闭包就是一个简单的对象,在函数式编程中可以用闭包来实现类似面向对象的一些功能,比如静态私有变量。
闭包产生的条件: 函数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本身就有静态变量,可以用静态变量达到闭包实现的静态私有变量的效果,其它用处我还没有研究,暂不清楚;
觉得文章对你有用的话鼓励一下我吧