作者:Saviio
链接:https://www.zhihu.com/question/34210214/answer/94933160
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

闭包其实是一个很通用的概念,正如 @寸志 老师说的:「闭包是词法作用域的体现」。 而很多大家耳熟能详的语言里都支持函数作为一类对象,比如JavaScript,Ruby,Python,C#,Scala,Java8.....,而这些语言里无一例外的都提供了闭包的特性,因为闭包可以大大的增强函数的处理能力,函数可以作为一类对象的这一优点才能更好的发挥出来。那什么是闭包呢,一言以蔽之:一个持有外部环境变量的函数就是闭包。 理解闭包通常有着以下几个关键点:1.函数2.自由变量3.环境举个栗子:let a = 1
let b = function(){

console.log(a)

}
在这个例子里函数b因为捕获了外部作用域(环境)中的变量a,因此形成了闭包。 而由于变量a并不属于函数b,所以在概念里被称之为「自由变量」。再举个栗子:function a(x, y){

console.log(x, y) //在这里,x和y都不是自由变量
function b(){
    console.log(x, y) //但在这个内部函数b中,x和y相对于b都是自由变量,而函数a的作用域则是环境。
}
//无论b最终是否会作为返回值被函数a返回,b本身都已经形成了闭包。

}
为了更直观的描述「捕获」这个过程,再用PHP来个栗子:function getMoney() {
$rmb = 1;
$func = function() use ( $rmb ) {

echo $rmb;

};
$func();
}
在这里 use 这个关键字直接对应的描述了「捕获」这一概念。在代码中,开发者需要显式的通知引擎:我需要捕获一个环境里的变量在之后的Function Block里使用。(当然,由于PHP需要手动声明,所以经常被人诟病说支持的不够自然。P.S:在一些语言里,「捕获」这一行为还有一个特点:闭包只会捕获自由变量的引用。这也是为什么大家在JS里写循环的事件绑定时,需要再额外的使用一个IIFE来包裹的原因。(用IIFE对自由变量的引用立即求值)所以其实闭包是一个比较直观的概念,不过之前有人问:如果这么解释,那和别的一些OO的语言看起来没区别啊,即使是早期的C#也可以像下面这么用,不也使用了函数作用域以外的变量吗Class A
{

private int x = 1; 
public int value()
{
    return x;
}

}
但实际,上面的代码是省略一部分状语的。编译器会知道,它表达的其实是如下的意思。Class B
{

private int x = 1; 
public int value()
{  
    return this.x;
}

}
变量x其实是实例上的成员。成员函数的使用必须依附于类的实例。当然如果用闭包的设计来理解的话也可以对应的理解成 「实例是其拥有的成员函数的环境」。(不过,你看一些以闭包为特性的语言比如JS里,函数可以捕获很多层作用域之外的变量,因此也较之有着更好的表达力。大抵就是这样。

发表评论