闭包不是某些编程语言的专属,它是一种编程模式。
不同的语言对闭包的支持和实现方式是不一样的,例如Groovy和JavaScript等动态语言可以实现方法级别的闭包,像java这样的语言只能从类级别开始实现。
先来看各种闭包如何实现:
javascript:
这段脚本里面,say函数中定义了con和end引用的匿名函数,这么做就称为闭包,其实意图很明显,只希望con和匿名函数在say函数中可见,对外部隐藏,当使用它们的时候,只需要把say()函数传递给调用者havefun()即可。形象的说,将其据为己有,画地为牢,打造自己内部的层次和结构,外部根本不知道其内部实现,这就是闭包的意义。在闭包里面的变量等结构仍然遵循作用域规则,如nick对于con()是全局变量等等,这些都和平常一样,不要认为是闭包的什么特点。
<script>
var fun = function say(){
var name = document.getElementById("n");
var nick = "dear";
con();
function con(){
alert(nick+name.innerHTML);
}
var end = function(arg){
nick = nick+arg;
alert(nick);
}("beauty")
}
function havefun(func){
func();
}
</script>
</head>
<body>
<p id="n" onclick="havefun(fun)">妹子</p>
groovy的闭包:http://blog.youkuaiyun.com/shewmi/article/details/78820031
对于闭包,网上很多人说法不一,总结如下几点:
1、有人说闭包是语法糖,函数式编程
2、有人说闭包是只处理唯一的任务
3、有人说闭包是用来共享函数局部变量
4、有人说闭包为了避免垃圾回收
5、有人说闭包是函数中定义函数
6、还有人说闭包是为了让外部访问内部的东西
我认为,这些说法都有其道理,但是都不准确,也不全面,比较狭隘。从上面的例子我已经说了闭包的意义是什么。但是在一些动态语言中,大多数是函数式编程,函数的粒度和级别太小,闭包的特性不是那么明显。我们来看一下高级特性:
java:
我们先定义一个Computer类,其中name、ram、kbs三个属性被设定为私有(外部不可见),另外方法设定为protected(仅同包的和其子类可见),那么这就是对computer的一个简单的封装,其实想想,闭包的思想和封装殊途同归,只是层次不一样,封装是面对各种类型的,如属性,方法,内部类等,而闭包则是封装的一个个体或每一道工序。在这个java类中,它的属性和方法就是进行了不同程度的闭包处理:
我们在另一个包中编写一个测试类,首先创建一个computer对象,然后调用其方法或属性,发现编译不能通过,这就是因为其属性和方法被闭包处理了,所以外部不能随意的直接操作computer的属性和方法:
那么在java中,使用匿名内部类可以让闭包的东西上升一个层次,所谓匿名内部类,其实就是这个computer类的子类,由于不需要类名所以称为匿名,并且因为它是子类,所以可以使用computer中闭包的属性和方法,来看:
输出:
也正因如此,有很多人认为闭包就是借用某个类的方法,这是不正确的,也并不是通过匿名内部类就可以打破这个约束,其实,上面的操作仍然是一个闭包。再来看:
上面的操作中,我们尝试在语句中抛出异常,然而编译不通过,因为匿名内部类中执行的代码块是对象实例初始化代码,在对象创建过程中出现异常,那么对象就不能正常创建,因此这是一个原子操作,要么成功,要么失败。你可能想到一点,虽然初始化代码块没有正常结束,但是new Computer()方法是调用了构造方法,这个应该会执行成功。的确,如上所述,对象仍然会被创建,如果这个异常不是抛出而是被捕获会怎样?
我们修改一下computer的方法,模拟中毒,所以我们也准备类另一个方法repair,用来杀毒:
显然,如果这个闭包的对象在初始化过程中出现了错误,那么它将报废,你只能重新创建computer对象并重新初始化它,也就是说,这个对象com是一次性的,一旦创建,只要离开了匿名内部类,你甭想在操作它或者修改,也不用担心别的地方会擅自修改或操作它,除非重新创建,但是它原来的数据别人已经拿不到了,这样一来就保证了数据内容的完整性和有效性。
再来看,想利用repair来杀毒也只能在内部类中来执行,或者由专业人士处理(同包的类或子类),这样也就相当于computer内部的操作。如:
到这里,闭包的作用应该清楚了,闭包在函数层面来说就是隐藏内部调用逻辑,允许内部进行结构化编写但不被外部所用。在面向对象的概念上来讲,其作用就是隐藏对象内部内容,防止外部进行直接操作,在一定程度上保证数据的完整和有效。闭包是封装的一个特写。但是正如责任链模式那样,闭包的东西总得有个开口跟外界交互,否则数据就是去了意义,但是内部的定义又不能被外部直接访问,看似矛盾,其实这正是迪米特法则(最少知道原则)的体现,闭包的内部都应当是服务于自身,而对外部是无须利用的,这种前提下才考虑使用闭包。如果内部逻辑是外部可以复用的,则应将其当分离解耦。
最后提一下,上面的例子中,并不是完全闭包,因为computer要跟外界交互,我们只需要适当的封装,然后公开一些入口给外部调用即可,因此封装要适度,过度的封装会影响代码结构和实现能力,所以完全的闭包一般是不会有的,如果我们将computer的类修改为final,那么他就不能被继承从而防止闭包被打破,闭包性进一步加强,而这样使用匿名内部类途径交互变为不可能,通常情况下,我们只需要在同包创建一个类来代理使用computer即可。