javascript闭包面试题为什么闭包函数不能迭代

一、函数名的运用(第一类对象)
&&&&&& 函数名是一个变量,但它是一个特殊的变量,与括号配合可以执行函数的变量。
1,函数名的内存地址:
def func():
print("呵呵")
print(func)
# 结果为:&function func at 0x99D8&
打印出func的内存地址
2,函数名可以赋值给其他变量:
def func():
print("呵呵")
# 把函数当成一个变量赋值给另一个变量
# 函数调用
3,函数名可以当做容器类的元素:
def func1():
print("呵呵")
def func2():
print("呵呵")
def func3():
print("呵呵")
lst = [func1 , func2 , func3]
for i in lst:
4,函数名可以当做函数的参数:
def func():
print("吃了么")
def func2(fn):
print("我是func2")
# 执行传递过来的fn
print("我是func2")
func2(func)
# 把函数名func当成参数传递给func2的参数fn
5,函数名可以作为函数的返回值:
def func_1():
print("这里是函数1")
def func_2():
print("这里是函数2")
print("这里是函数1")
return func_2
fn = func_1()
# 执行函数1,函数1返回的是函数2,这时fn指向的就是上面函数2
# 结果为:
# 这里是函数1
# 这里是函数1
# 执行上面返回的函数2
# 结果为:
# 这里是函数2
什么是闭包?闭包就是内层函数对外层函数(非全局)的变量的引用,如下示例:
def func():
def inner():
# 结果为:10
我们可以用__closure__来检测函数是不是闭包,函数名.__closure__返回cell就是闭包,返回None就不是闭包。
def func():
def inner():
print(inner.__closure__)
# (&cell at 0x75B8: int object at 0xE26D40&,)
问题:如何在函数外边调用内部函数呢?具体示例如下:
def outer():
a = 'hello'
# 内部函数
def inner():
return inner
fn = outer()
# 访问外部函数,获取到内部函数的内存地址
# 访问内部函数
那如果多层嵌套呢?很简单,只需要一层一层的往外层返回就行了,具体示例如下:
def func1():
def func2():
def func3():
print("嘿嘿")
return func3
return func2
func1()()()
# 执行func3,结果为:嘿嘿
由它我们可以引出闭包的好处,由于我们在外界可以访问内部函数,那这个时候内部函数访问的时间和时机就不一定了,因为在外部,我可以选择在任意的时间去访问内部函数,这个时候,想一想,我们之前说过,如果一个函数执行完毕,则这个函数中的变量以及局部命名空间中内容将会被销毁,在闭包中,如果变量被销毁了,那内部函数将不能正常执行,所以,python规定:如果你在内部函数中访问了外层函数中的变量,那么这个变量将不会消亡,将会常驻在内存中,也就是说,使用闭包,可以保证外层函数中的变量在内存中常驻。这样做有什么好处呢?有非常大的好处,下面来看一个简单的关于爬虫的代码:
from urllib.request import urlopen
def but():
content = urlopen("http://www.xiaohua100.cn/index.html").read()
def get_content():
return content
return get_content
fn = but()
# 这个时候就开始加载校花100的内容
# 后面需要用到这里面的内容就不需要再执行非常耗时的网络连接操作了
content = fn()
# 获取内容
print(content)
content2 = fn()
# 重新获取内容
print(content2)
综上可得:闭包的作用就是让一个变量能够常驻内存,供后面的程序使用。
三,迭代器
&&&&&&&我们之前一直在用可迭代对象进行迭代操作,那么到底什么是可迭代对象?首先我们先回顾一下目前我们所熟知的可迭代对象有哪些,有str,list,tuple,dict,set,那为什么我们可以称它们为可迭代对象呢?因为它们都遵循了可迭代协议,什么是可迭代协议?首先我们看下面一段错误代码:
for c in s:
for i in 123:
# Traceback (most recent call last):
File "E:/pythonDemo/1-basis/test10.py", line 107, in &module&
for i in 123:
# TypeError: 'int' object is not iterable
注意看报错信息中有这样一句话:'int' object is not iterable,翻译过来就是整数类型对象是不可迭代的,iterable表示可迭代的,表示可迭代协议,那么如何进行验证你的数据类型是否符合可迭代协议,我们可以通过dir函数来查看类中定义好的所有方法。具体示例如下:
s = "我的哈哈哈"
print(dir(s))
# 可以打印对象中的方法和函数
print(dir(str))
# 也可以打印类中声明的方法和函数
在打印结果中,寻找__iter__,如果能找到,那么这个类的对象就是一个可迭代对象。我们发现在字符串中可以找到__iter__,继续看一下list,tuple,dict,set,具体如下:
print(dir(list))
print(dir(tuple))
print(dir(dict))
print(dir(set))
print(dir(open("test9.py")))
# 文件对象
我们发现这几个可以进行for循环的东西都有__iter__函数,包括range也有,可以自己试一下。
综上可知,通过寻找__iter__可以查看一个对象是否是可迭代对象,除此之外,我们还可以通过isinstence()函数来查看一个对象是什么类型的,具体示例如下:
ls = [1,2,3]
ls_iter = ls.__iter__()
# 获取列表ls的迭代器
from collections import Iterable
from collections import Iterator
print(isinstance(ls,Iterable))
ls是可迭代对象的一个实例
print(isinstance(ls,Iterator))
ls不是迭代器的一个实例
print(isinstance(ls_iter,Iterable))
ls_iter是可迭代对象的一个实例
print(isinstance(ls_iter,Iterator))
ls_iter是迭代器的一个实例
综上,我们可以确定,如果对象中有__iter__函数,那么我们认为这个对象遵守了可迭代协议,就可以获取到相应的迭代器,上面代码中的__iter__就是帮我们获取到对象的迭代器,我们使用迭代器中的__next__()来获取到一个迭代器中的元素,那么我们之前讲的for循环的工作原理到底是什么呢?继续看下面代码:
s = "我爱北京天安门"
c = s.__iter__()
# 获取迭代器
print(c.__next__())
# 使用迭代器进行迭代,获取一个元素:我
print(c.__next__())
print(c.__next__())
print(c.__next__())
print(c.__next__())
print(c.__next__())
print(c.__next__())
print(c.__next__())
# StopIteration
for循环如下:
for i in [1,2,3]:
下面使用while循环+迭代器来模拟for循环(必须要掌握):
lst = [1,2,3]
lst_iter = lst.__iter__()
i = lst_iter.__next__()
except StopIteration:
&&&&&& Iterable:可迭代对象,内部包含__iter__()函数,不包含__next__()函数;
&&&&&& Iterator:迭代器,内部包含__iter__()函数,同时包含__next__()函数;
&&&&&&&迭代器特点:
&&&&&&&&&&&&& 1,节省内存(下篇生成器中介绍);
&&&&&&&&&&&&& 2,惰性机制(遇到__next__才取一个);
&&&&&&&&&&&&& 3,不能反复,只能向下执行;
&&&&&&&我们可以把要迭代的内容当成子弹,然后呢,获取到迭代器__iter__(),就把子弹都装在弹夹中,然后发射就是__next__()把每一个子弹(元素)打出来,也就是说,for循环的时候,一开始是__iter__()来获取迭代器,后面每次获取元素都是通过__next__()来完成的,当程序遇到StopIteration将结束循环。
阅读(...) 评论()for (var i = 1; i &= 5; i++) {
setTimeout( function timer() {
console.log(i);
}, i*1000)
输出结果:
当时间是固定的数,如0、,执行结果就是0、1、6秒后,一次输出五个6;
当时间是 i*1000, 输出是:每隔1秒,输出一个6,共5次。
代码中到底有什么‘缺陷’,导致它的行为与 语义暗示的不一致呢?
缺陷 是 我们试图假设
循环中的每一个迭代在运行时,都会给自己’捕获’一个i的副本。但是,根据作用域的工作原理,实际情况是,尽管循环中的五个函数是在各个迭代中分别定义的,但是它们都被封闭在一个共享的全局作用域中,因此实际上只有一个 i。
这样说的话,当然所以函数共享一个 i 的引用。
循环结构让我们误以为背后还有更复杂的机制在起作用,实际上并没有。如果将延迟函数的回调重复定义5次,完全不使用循环,那它同这段代码是完全等价的。
----《你不知道的JS 上卷》p49
show me the code
第一种情况
setTimeout()第二个参数是个常数
书中那段话就是说,
for (var i = 1; i &= 5; i++) {
setTimeout( function timer() {
console.log(i);
var i = 1;
var i = 2;
var i = 3;
var i = 4;
var i = 5;
var i = 6;
console.log(i);
console.log(i);
console.log(i);
console.log(i);
console.log(i);
console.log(i);
输出情况:
当时间是固定的数,如0、,执行结果就是0、1、6秒后,一次输出五个6;
等到这些回调执行时,i的值就是6
第二种情况
setTimeout()第二个参数含有变量
for (var i = 1; i &= 5; i++) {
setTimeout( function timer() {
console.log(i);
}, i*1000)
var i = 1;
var i = 2;
var i = 3;
var i = 4;
var i = 5;
var i = 6;
console.log(i);
console.log(i);
console.log(i);
console.log(i);
console.log(i);
console.log(i);
输出情况:
当时间是 i*1000, 输出是:每隔1秒,输出一个6,共5次。
因为等到这些回调执行时,i的值就是6
HOW TO FIX IT
我知道,闭包!
for (var i = 1; i &= 5; i++) {
(function() {
setTimeout( function timer() {
console.log(i);
}, i*1000)
然并卵,还是一秒一个6,五次
因为,新加上的 IIFE(创建并立即执行) 作用域是”空的”,它并没有自己的变量。执行栈清空后,线程从任务队列里读取回调函数,它们还是引用那个唯一的全局变量i。
正确的闭包姿势:
通过在闭包作用域中添加自己的变量,从而在每次迭代中,捕获i的副本。
for (var i = 1; i &= 5; i++) {
(function() {
setTimeout( function timer() {
console.log(j);
}, j*1000)
更简洁的姿势:
for (var i = 1; i &= 5; i++) {
(function(j) {
setTimeout( function timer() {
console.log(j);
}, j*1000)
由此,能够输出:1 2 3 4 5,一秒一个
ES6的打开方式: 块作用域
for (var i = 1; i &= 5; i++) {
setTimeout( function timer() {
console.log(j);
}, j*1000)
或直接在for循环头部里,每次迭代都声明一次
for (let i = 1; i &= 5; i++) {
setTimeout( function timer() {
console.log(i);
}, i*1000)
《JavaScript高级程序设计第三版》 p181
var a = function ceateFunctions() {
var result = new Array();
for (var i = 0; i & 10; i++) {
result[i] = function () {
return result
console.log(a()[0]());
//0 - 9 输出都是10
道理一样。
都是因为引用同一个i,且没能在迭代中捕获i的副本,或者没能在迭代中及时按当时的值执行。直到i早都变成10了,才执行,RHS引用的结果当然是i此刻的值,即10。
var a = function ceateFunctions() {
var result = new Array();
for (var i = 0; i & 10; i++) {
result[i] = (function(num) {
return function() {
console.log(a()[0]());
相关文章:
Javascript中的闭包——JS高程第三版第七章知识总结
Javascript中的闭包——JS高程第三版第七章知识总结
JavaScript闭包最简洁实用讲解
在学习JS的路上,无论是实际应用还是面试,闭包肯定是一个绕不过去的技术点。在我的学习生涯中,闭包的难度其实不大,但因其涉及的技术点和应用比较多,理解起来总有一种“管中窥豹”未见其真身的感觉。读了廖雪峰...
我在理解高程上讲的闭包的时候 ,理解上产生过许多错误,我们首先来看高程上的函数
function opp() {
var result =
for (var i=...
闭包问题-及js高程中闭包与变量例题解析
闭包是js中的重点,也是一个较难理解的一个点,但是其应用却很广,故而理解并正确使用闭包是很有必要的。
定义:有权访问另一个函数作用域中的变量的函数。
如下,我想获取 tes...
window对象
在网页中定义的任何一个对象、变量和函数,都以window作为其global对象,因此有权访问parseInt()
全局变量不能通过delete操作符删除,而直接在wind...
JavaScript是一种非常强大的函数式编程语言,可以动态创建函数对象。
由于JavaScript还支持闭包(Closure),因此,函数可以引用其作用域外的变量,非常强大。
来看看在JavaS...
初步理解闭包
1.什么是闭包
借用MDN的定义
闭包是指这样的*作用域*,它包含有一个*函数*,这个函数可以调用被这个作用域所*封闭*的变量、函数或者闭包等内容。通常我们通过闭包所对应的函数来获得对闭...
没有更多推荐了,&script type="text/javascript"&
//五个迭代方法 都接受两个参数:要在每一项上运行的函数 和 运行该函数的作用域(可选)
//every():对数组中的每一项运行给定函数。如果函数对每一项都返回true,则返回true。
//filter():对数组中的每一项运行给定函数。返回该函数会返回true的项组成的数组。
//forEach():对数组中每一项运行给定函数。该函数没有返回值。
//map():对数组中每一项运行给定函数。返回每次函数调用的结果组成的函数。
//some():对数组中每一项运行给定函数。如果函数对 任一项返回true,则返回true。
var numbers = [1, 2, 3, 4, 5, 4, 3, 2, 1];
//every()和some()最相似
item:当前遍历项,index:当前项索引,array:数组对象本身
var everyResult = numbers.every(function (item, index, array) {
return item & 2;
alert(everyResult);//false
var someResult = numbers.some(function (item, index, array) {
return item & 2;
alert(someResult);//true
var filterResult = numbers.filter(function (item, index, array) {
return item & 2;
alert(filterResult);//[3,4,5,4,3]
var mapResult = numbers.map(function (item, index, array) {
return (item * 2);
alert(mapResult);//[2,4,6,8,10,8,6,4,2]
//forEach 本质上和for循环没有区别
var forEachResult=numbers.forEach(function(item,index,array){
alert(item)
阅读(...) 评论()您现在的位置:&&&&&&&&&&&&文章内容
快捷导航:
好处不止一点点编程结构--闭包
来源:考试大&&&【考试大:中国教育考试第一门户】&&&日
闭包是可以用作函数参数和方法参数的代码块。一直以来,这种编程结构都是一些语言(如 Lisp、Smalltalk 和 Haskell)的重要组成部分。尽管一些颇具竞争力的语言(如 C#)采纳了闭包,但 Java 社区至今仍抵制对它的使用。本文探讨闭包在为编程语言带来一点点便利的同时是否也带来不必要的复杂性、闭包还有无更多的益处。
10 年前,我刚刚开始山地自行车运动的时候,我更愿意选用零件尽可能少尽可能简单的自行车。稍后,我意识到一些零件(如后减震器)可以保护我的背部和我自行车的框架在德克萨斯州高低起伏的山区中免受损害。我于是可以骑得更快,出问题的次数也渐少。虽然随之带来了操作上的复杂性和维护需求的增加,但对于我来说这点代价还是值得的。
关于本系列
在 跨越边界系列 文章中,作者 Bruce Tate 提出这样一种观点,即当今的 Java 程序员们通过学习其他方法和语言很好地武装了自己。自从 Java 技术明显成为所有开发项目的最佳选择以来,编程前景得以改变。其他框架影响着 Java 框架的构建方式,您从其他语言中学到的概念也可以影响 Java 编程。您编写的 Python(或 Ruby、Smalltalk 等语言)代码可以改变编写 Java 代码的方式。
本系列介绍与 Java 开发完全不同的编程概念和技术,但是这些概念和技术也可以直接应用于 Java 开发。在某些情况下,需要集成这些技术来利用它们。在其他情况下,可以直接应用这些概念。具体的工具并不那么重要,重要的是其他语言和框架可以影响 Java 社区中的开发人员、框架,甚至是基本方式。
关于闭包这个问题,Java 爱好者们现在陷入了类似的争论中。一些人认为闭包带给编程语言的额外复杂性并不划算。他们的论点是:为了闭包带来的一点点便利而打破原有语法糖的简洁性非常不值得。其他一些人则认为闭包将引发新一轮模式设计的潮流。要得到这个问题的最佳答案,您需要跨越边界,去了解程序员在其他语言中是如何使用闭包的。
Ruby 中的闭包
闭包是具有闭合作用域 的匿名函数。下面我会详细解释每个概念,但最好首先对这些概念进行一些简化。闭包可被视作一个遵循特别作用域规则且可以用作参数的代码块。我将使用 Ruby 来展示闭包的运行原理。用 irb 命令启动解释程序,然后用 load filename 命令加载每个样例。清单 1 是一个最简单的闭包: 清单 1. 最简单的闭包
3.times {puts "Inside the times method."}
Inside the times method.
Inside the times method.
Inside the times method.
times 是作用在对象 3 上的一个方法。它执行三次闭包中的代码。{puts "Inside the times method."} 是闭包。它是一个匿名函数,times 方法被传递到该函数,函数的结果是打印出静态语句。这段代码比实现相同功能的 for 循环(如清单 2 所示)更加紧凑也更加简单: 清单 2: 不含闭包的循环
for i in 1..3
puts "Inside the times method."
Ruby 添加到这个简单代码块的第一个扩展是一个参数列表。方法或函数可通过传入参数与闭包通信。在 Ruby 中,使用在 || 字符之间用逗号隔开的参数列表来表示参数,例如 |argument, list|。用这种方法使用参数,可以很容易地在数据结构(如数组)中构建迭代。清单 3 显示了在 Ruby 中对数组进行迭代的一个例子: 清单 3. 使用了集合的闭包
['lions', 'tigers', 'bears'].each {|item| puts item}
each 方法用来迭代。您通常想要用执行结果生成一个新的集合。在 Ruby 中,这种方法被称为 collect。您也许还想在数组的内容里添加一些任意字符串。清单 4 显示了这样的一个例子。这些仅仅是众多使用闭包进行迭代的方法中的两种。 清单 4. 将参数传给闭包
animals = ['lions', 'tigers', 'bears'].collect {|item| item.upcase}
puts animals.join(" and ") + " oh, my."
LIONS and TIGERS and BEARS oh, my.
在清单 4 中,第一行代码提取数组中的每个元素,并在此基础上调用闭包,然后用结果构建一个集合。第二行代码将所有元素串接成一个句子,并用 " and " 加以分隔。到目前为止,介绍的还都是语法糖而已。所有这些均适用于任何语言。
到目前为止看到的例子中,匿名函数都只不过是一个没有名称的函数,它被就地求值,基于定义它的位置来决定它的上下文。但如果含闭包的语言和不含闭包的语言间惟一的区别仅仅是一点语法上的简便 ―― 即不需要声明函数 ―― 那就不会有如此多的争论了。闭包的好处远不止是节省几行代码,它的使用模式也远不止是简单的迭代。
闭包的第二部分是闭合的作用域,我可以用另一个例子来很好地说明它。给定一组价格,我想要生成一个含有价格和它相应的税金的销售-税金表。我不想将税率硬编码到闭包里。我宁愿在别处设置税率。清单 5 是可能的一个实现: 清单 5. 使用闭包构建税金表
tax = 0.08
prices = [4.45, 6.34, 3.78]
tax_table = prices.collect {|price| {:price =& price, :tax =& price * tax}}
tax_table.collect {|item| puts "Price: #{item[:price]}
Tax: #{item[:tax]}"}
Price: 4.45
Tax: 0.356
Price: 6.34
Tax: 0.5072
Price: 3.78
Tax: 0.3024
在讨论作用域前,我要介绍两个 Ruby 术语。首先,symbol 是前置有冒号的一个标识符。可抽象地把 symbol 视为名称。:price 和 :tax 就是两个 symbol。其次,可以轻易地替换字符串中的变量值。第 6 行代码的 puts "Price: #{item[:price]} Tax: #{item[:tax]}" 就利用了这项技术。现在,回到作用域这个问题。
请看清单 5 中第 1 行和第 4 行代码。第 1 行代码为 tax 变量赋了一个值。第 4 行代码使用该变量来计算价格表的税金一栏。但此项用法是在一个闭包里进行的,所以这段代码实际上是在 collect 方法的上下文中执行的!现在您已经洞悉了闭包 这个术语。定义代码块的环境的名称空间和使用它的函数之间的作用域本质上是一个作用域:该作用域是闭合的。这是个基本特征。这个闭合的作用域是将闭包同调用函数和定义它的代码联系起来的纽带。
用闭包进行定制
您已经知道如何使用现成的闭包。Ruby 让您也可以编写使用自己的闭包的方法。这种自由的形式意味着 Ruby API 的代码会更加紧凑,因为 Ruby 不需要在代码中定义每个使用模型。您可以根据需要通过闭包构建自己的抽象概念。例如,Ruby 的迭代器数量有限,但该语言没有迭代器也运行得很好,这是因为可以通过闭包在代码中构建您自己的迭代概念。
要构建一个使用闭包的函数,只需要使用 yield 关键字来调用该闭包。清单 6 是一个例子。paragraph 函数提供第一句和最后一句输出。用户可以用闭包提供额外的输出。
清单 6. 构建带有闭包的方法
def paragraph
puts "A good paragraph should have a topic sentence."
puts "This generic paragraph has a topic, body, and conclusion."
paragraph {puts "This is the body of the paragraph."}
A good paragraph should have a topic sentence.
This is the body of the paragraph.
This generic paragraph has a topic, body, and conclusion.
通过将参数列表附加给 yield,很容易利用定制闭包中的参数,如清单 7 中所示。
清单 7. 附加参数列表
def paragraph
"A good paragraph should have a topic sentence, a body, and a conclusion. "
conclusion = "This generic paragraph has all three parts."
puts topic
yield(topic, conclusion)
puts conclusion
paragraph do |topic, conclusion|
puts "This is the body of the paragraph. "
c = conclusion
puts "The topic sentence was: '#{t}'"
puts "The conclusion was: '#{c}'"
不过,请认真操作以保证得到正确的作用域。在闭包里声明的参数的作用域是局部的。例如,清单 7 中的代码可以运行,但清单 8 中的则不行,原因是 topic 和 conclusion 变量都是局部变量:
清单 8. 错误的作用域
def paragraph
"A good paragraph should have a topic sentence."
conclusion = "This generic paragraph has a topic, body, and conclusion."
puts topic
yield(topic, conclusion)
puts conclusion
my_topic = ""
my_conclusion = ""
paragraph do |topic, conclusion|
# these are local in scope
puts "This is the body of the paragraph. "
my_typic = topic
my_conclusion = conclusion
puts "The topic sentence was: '#{t}'"
puts "The conclusion was: '#{c}'"
闭包的应用
下面是一些常用的闭包应用:
当您可以用一种简单便利的方式构建自己的闭包时,您就找到了能带来更多新可能性的技术。重构能将可以运行的代码变成运行得更好的代码。大多数 Java 程序员都会从里到外 进行重构。他们常在方法或循环的上下文中寻找重复。有了闭包,您也可以从外到里 进行重构。
用闭包进行定制会有一些惊人之处。清单 9 是 Ruby on Rails 中的一个简短例子,清单中的闭包用于为一个 HTTP 请求编写响应代码。Rails 把一个传入请求传递给控制器,该控制器生成客户机想要的数据(从技术角度讲,控制器基于客户机在 HTTP accept 头上设置的内容来呈现结果)。如果您使用闭包的话,这个概念很好理解。
清单 9. 用闭包来呈现 HTTP 结果
@person = Person.find(id)
respond_to do |wants|
wants.html { render :action =& @show }
wants.xml { render :xml =& @person.to_xml }
清单 9 中的代码很容易理解,您一眼就能看出这段代码是用来做什么的。如果发出请求的代码块是在请求 HTML,这段代码会执行第一个闭包;如果发出请求的代码块在请求 XML,这段代码会执行第二个闭包。您也能很容易地想象出实现的结果。wants 是一个 HTTP 请求包装程序。该代码有两个方法,即 xml 和 html,每个都使用闭包。每个方法可以基于 accept 头的内容选择性地调用其闭包,如清单 10 所示:
清单 10. 请求的实现
yield if self.accept_header == "text/xml"
yield if self.accept_header == "text/html"
到目前为止,迭代是闭包在 Ruby 中最常见的用法,但闭包在这方面的用法远不止使用集合内置的闭包这一种。想想您每天使用的集合的类型。XML 文档是元素集。Web 页面是特殊的 XML 集。数据库由表组成,而表又由行组成。文件是字符集或字节集,通常也是多行文本或对象的集合。Ruby 在闭包中很好地解决了这几个问题。您已经见过了几个对集合进行迭代的例子。清单 11 给出了一个对数据库表进行遍历的示例闭包:
清单 11. 对数据库的行进行遍历
require 'mysql'
db=Mysql.new("localhost", "root", "password")
db.select_db("database")
result = db.query "select * from words"
result.each {|row| do_something_with_row}
清单 11 中的代码也带出了另一种可能的应用。MySQL API 迫使用户建立数据库并使用 close 方法关闭数据库。实际上可以使用闭包代替该方法来建立和清除资源。Ruby 开发人员常用这种模式来处理文件等资源。使用这个 Ruby API,无需打开或关闭文件,也无需管理异常。File 类的方法会为您处理这一切。您可以使用闭包来替换该方法,如清单 12 所示:
清单 12. 使用闭包操作 File
File.open(name) {|file| process_file(f)}
闭包还有一项重大的优势:让实施策略变得容易。例如,若要处理一项事务,采用闭包后,您就能确保事务代码总能由适当的函数调用界定。框架代码能处理策略,而在闭包中提供的用户代码能定制此策略。清单 13 是基本的使用模式:
清单 13. 实施策略
def do_transaction
setup_transaction
commit_transaction
roll_back_transaction
Java 语言中的闭包
Java 语言本身还没有正式支持闭包,但它却允许模拟闭包。可以使用匿名的内部类来实现闭包。和 Ruby 使用这项技术的原因差不多,Spring 框架也使用这项技术。为保持持久性,Spring 模板允许对结果集进行迭代,而无需关注异常管理、资源分配或清理等细节,从而为用户减轻了负担。清单 14 的例子取自于 Spring 框架的示例宠物诊所应用程序:
清单 14. 使用内部类模拟闭包
JdbcTemplate template = new JdbcTemplate(dataSource);
final List names = new LinkedList();
template.query("SELECT id,name FROM types ORDER BY name",
new RowCallbackHandler() {
public void processRow(ResultSet rs)
throws SQLException
names.add(rs.getString(1));
编写清单 14 中的代码的程序员不再需要做如下这些事:
处理数据库-依赖性问题
程序员们不用再为这些问题烦恼,因为该框架会处理它们。但匿名内部类只是宽泛地近似于闭包,它们并没有深入到您需要的程度。请看清单 14 中多余的句子结构。这个例子中的代码至少一半是支持性代码。匿名类就像是满满一桶冰水,每次用的时候都会洒到您的腿上。多余句子结构所需的过多的额外处理阻碍了对匿名类的使用。您迟早会放弃。当语言结构既麻烦又不好用时,人们自然不会用它。缺乏能够有效使用匿名内部类的 Java 库使问题更为明显。要想使闭包在 Java 语言中实践并流行起来,它必须要敏捷干净。
过去,闭包绝不是 Java 开发人员优先考虑的事情。在早期,Java 设计人员并不支持闭包,因为 Java 用户对无需显式完成 new 操作就在堆上自动分配变量心存芥蒂(参见 参考资料)。 如今,围绕是否将闭包纳入到基本语言中存在极大的争议。最近几年来,动态语言(如 Ruby、JavaScript,甚至于 Lisp )的流行让将闭包纳入 Java 语言的支持之声日益高涨。从目前来看,Java 1.7 最终很可能会采纳闭包。只要不断跨越边界,总会好事连连。
来源:考试大-
责编:qinqin&&&
模板不存在,请添加模板后再生成!E:\wwwroot\www_233_com\Templets\three\Templets\soft\small\cont_b_st.htm
暂无跟贴,欢迎您发表意见
考试大Java认证评论排行
1.&&评论4条
2.&&评论2条
3.&&评论2条
4.&&评论2条
5.&&评论1条
6.&&评论1条
12345678910
12345678910
123456SCJP考试题310-025(第二套)19-50/1477SCJP考试题310-025(第二套)51-91/147
12345678910}

我要回帖

更多关于 深入理解javascript闭包 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信