如有任何疑问,请参阅:http://www.martinfowler.com/bliki/Closure.html
随着人们对动态语言兴趣的增加,更多的人开始关注一个被称之为闭包(closures)或者块区(blocks)的概念,
有c/c++/java/c#(这些语言不支持闭包)背景的程序员并不知道闭包是什么。下面简单的解释了这个概念。
闭包出现迄今为止有一段时间了,我第一次使用闭包是在SmallTalk上,当时被称之为块(block),Lisp使用了闭包,他们同样出现在Ruby脚本语言之中,这也是大部分Ruby爱好者使用Ruby进行脚本编程的原因。
闭包的核心意义是一段可以作为参数被其他函数调用的代码,我将用一个简单的例子来说明,假设我有一批工人的名单,而我想得到其中同时又身为管理人员的那部分工人的名单,对于这部分工人我用一个isManager来标记,使用C#,我们可以这样来编码:
public static IList Managers(IList emps) {
IList result = new ArrayList();
foreach(Employee e in emps)
if (e.IsManager) result.Add(e);
return result;
}
而在支持闭包的语言中,我们可以用另一种写法,比如在Ruby中,我们可以这样:
def managers(emps)
return emps.select {|e| e.isManager}
end
select是定义在Ruby集合类中的一个方法,接受一段代码,一个闭包,作为它的参数,在ruby中你(In ruby you write that block of code between curlies (not the only way))。如果这段代码接受任何你在事先在“|”中间声明的参数,select迭代作为参数的数组,对数组中每一个元素执行那一段代码,将所有返回结果确定为true的元素放入一个数组中,然后返回这个数组。
看完了这个例子,如果你是一名C程序员你可能会觉得你能够使用指针做同样的事情,如果你是一名JAVA程序员你会认为你能够使用匿名内部类实现,如果是C#程序员的话你可能会考虑代理(delegate),这些机制类似于闭包,但是他们有2个不同的地方。
首先是形似上的不同,闭包的内部可以拥有一个本地变量,考虑下面定义的方法:
def highPaid(emps)
threshold = 150
return emps.select {|e| e.salary > threshold}
end
你可以看到在select代码块中有一个变量是在本方法中定义的,而在许多不支持闭包的语言中并不能像这样做,利用闭包你可以做更加有趣的事情,看看下面的函数:
def paidMore(amount)
return Proc.new {|e| e.salary > amount}
end
这个函数返回了一个闭包,在个函数的行为依赖于你传递给它的参数,我可以穿入一个值作为参数来创建一个这样的函数。
highPaid = paidMore(150)
highPaid包含了一段代码(在Ruby中被称为Proc),这段代码根据测试参数对象的工资是否超过150而返回true或者false,我可以这样来使用它:
john = Employee.new
john.salary = 200
print highPaid.call(john)
highPaid.call(john)调用我们前面定义过的代码e.salary>amount,这这里amount是150,这是我们前面创建该Proc时定义好的,当我调用print函数的时候,即使150已经超出了这个范围,但这个绑定依然有效。(Even if that 150 value went out of scope when I issue the print call, the binding still remains.)
所以第一个关键的地方是:闭包是一段代码加上对它们所在环境的绑定,这是使得闭包同C中的函数指针和其它语言中类似机制(比如java中的匿名内部类和C#中的代理)区别开来的一个因素。(java中只有final匿名内部类可以访问本地变量)
如果你不经过大量的实践的话,第二个不同点看起来并不如第一个不同点那么明显,但照样是也是非常重要的原因。支持闭包的语言允许你用很少量的语法来定义它们,这点看起来并不重要,但我认为很关键。这是为什么频繁运用闭包的原因,看看LISP,SMALLTALK,或者RUBY代码,你将会看到到处都在使用闭包语法--比起其他的语言中使用类似语法要频繁得多。能够访问本地变量和绑定运行上下文是一个原因,但我认为更重要的原因是这样使得代码更加简单清晰。
我来举一个很恰当的例子,将一个SmallTalk闭包语法的例子引入JAVA中。起初大部分人,包括我在内,都会使用匿名内部类去取代SMALLTALK中的闭包代码块,但最终的代码往往会变得非常丑陋和混乱,所以我们不得不放弃。
就像软件行业中其他的术语一样,人们并不能给出关于闭包的精确定义。一些人认为闭包仅仅应用于一个包括对自身环境绑定的实际值(the term only applies to an actual value that includes bindings from its environment),就像前述中highPaid函数所返回的那个值,另一些人认为闭包是指有能力绑定所在环境的程序构造(Others use the term 'closure' to refer to a programming construct that has the ability to bind to its environment),This debate, an example of the TypeInstanceHomonym, is usually carried out with the politeness and lack of pedantry that our profession is known for.
我经常在RUBY中用到闭包,但是我并不倾向于创建Procs and pass them around.大多数时候我使用闭包是基于围绕CollectionClosureMethods ,类似于前面所看到的select方法,另一个常用法是围绕方法执行,例如当我们处理一个文件。
File.open(filename) {|f| doSomethingWithFile(f)}
这里有一个open方法打开文件,执行代码块,然后关闭文件,This can be a very handy idiom for things like transactions (remember to commit or rollback) or indeed anything where you have to remember to do something at the end. I use this extensively in my xml transformation routines.
闭包的这种用法比起人们在LISP和函数式语言中所做的,其实是用的很少的,但即使是使用得少,当我使用不支持他们的语言编程的时候,我还是非常思念他们。闭包,当你第一次遇见它时它显得微不足道,但很快你就会喜欢上它。
分享到:
- 2008-07-11 09:21
- 浏览 2171
- 评论(0)
- 论坛回复 / 浏览 (0 / 2888)
- 查看更多
相关推荐
closure-compiler-npm, 用于管理和记录关闭编译器的包,通过npm使用 google-closure-compiler 用闭包编译器检查。编译。优化和压缩 Javascript这个库跟踪发布到 npmjs.org 和相关插件的相关问题。 任何与插件无关的...
前端开源库-closure-loader闭包加载器,用于Google闭包库依赖项的Webpack加载器
Laravel开发-closure-table Laravel的邻接表闭包表数据库设计模式实现
closure-stylesheets, lints,优化和 i18n izes的CSS transpiler 关闭样式表闭包样式表是对CSS的扩展,它向标准 CSS conditionals conditionals conditionals conditionals conditionals conditionals conditionals
Swift3.0 闭包整理 - CocoaChina_让移动开发更简单1
google-closure-library-v20180405-50-gda9add3.zip
离散数学中闭包运算实验,在。net,使用了Warshall算法
数据库老师要求用代码实现求属性闭包,该代码为python代码,注释详细
Python闭包实例closure.py 简单示例闭包的使用 简单示例闭包的使用
grunt-closure-tools npm install grunt-closure-tools --save-dev 然后通过grunt.js添加到grunt.js来注册任务: grunt . loadNpmTasks ( 'grunt-closure-tools' ) ; Grunt 0.3.x 兼容性 获得咕噜声 0.3.x。 兼容...
npm install superstartup-closure-compiler --save-deps --silent 几乎没有atm,只有两种方法: 获取路径() 获取闭包编译器.jar文件的相对路径。 获取路径SS() 获取超级启动编译器.jar文件的相对路径。 例子: var...
主包装google-closure-compiler软件包包含Grunt和Gulp插件以及一个CLI: 其他套餐裸露的发行版面向希望在特定平台上进行创作的开发人员。 Java版本: 本机Linux版本: 本机OSX构建: 本机Windows版本: 执照版权所有...
闭包编译器 Maven 插件 Maven 插件,用于使用 Google 的 Closure 编译器编译/压缩 JavaScript 代码
闭包编译器是一个让javascript下载和运行更快的工具。它是一个真正的javascript编译器。它不是从源语言编译成机器代码,而是从javascript编译成更好的javascript。它解析你的javascript,分析它,删除死代码,重写并...
闭包编译器 什么是闭包编译器? 来自: Closure Compiler 是一个让 JavaScript 下载和运行速度更快的工具。 它是一个真正的 JavaScript 编译器。 它不是从源语言编译为机器代码,而是从 JavaScript 编译为更好的 ...
求文法的closure闭包,针对每个产生式,求其closure闭包,并打印输出
主要介绍了php的instanceof和判断闭包Closure操作,结合实例形式分析了PHP使用instanceof判断类实例以及判断闭包Closure相关操作技巧,需要的朋友可以参考下
node --preserve-symlinks node_modules/closure-calculate-chunks/index.js --entrypoint ./src/js/entry.js 注意:应使用--preserve-symlinks选项启动使用该库的节点进程,否则返回的文件路径可能与节点模块解析...