我已经阅读了很多关于闭包的文章,而且,令人尴尬的是,我仍然不明白这个概念!文章解释了如何用一些例子来创建一个闭包,但我没有看到任何关注它们的重点,因为它们在很大程度上看起来是人为的例子。我并不是说所有这些都是人为的,只是我发现它们看起来很做作,而且我知道在理解它们之后,我将能够使用它们。所以为了理解闭包,我正在研究一些真正的问题,可以使用闭包很自然地解决。
例如,解释一个人递归的一种自然方法可能是解释n!的计算。理解像使用递归计算数字的阶乘的问题是很自然的。类似地,通过读取每个元素并与所讨论的数字进行比较,在未排序的数组中找到元素几乎是明智的。此外,在不同的层面上,进行面向对象的编程也很有意义。
所以我试图找到一些可以在有或没有闭包的情况下解决的问题,但是使用闭包可以更好地思考它们并解决它们。此外,闭包有两种类型,每次调用闭包都可以创建环境变量的副本,或引用相同的变量。那么什么样的问题可以更自然地解决哪些闭包实现?
答案 0 :(得分:3)
回调就是一个很好的例子。我们来看看JavaScript。
想象一下,您有一个新闻网站,标题和简短的模糊以及每个网站旁边的“阅读更多...”按钮。当用户单击该按钮时,您希望异步加载与所单击按钮对应的内容,并向用户显示所请求标题旁边的指示符,以便用户可以“看到正在其中工作的页面”。
function handleClickOnReadMore(element) {
// the user clicked on one of our 17 "request more info" buttons.
// we'll put a whirly gif next to the clicked one so the user knows
// what he's waiting for...
spinner = makeSpinnerNextTo(element);
// now get the data from the server
performAJAXRequest('http://example.com/',
function(response) {
handleResponse(response);
// this is where the closure comes in
// the identity of the spinner did not
// go through the response, but the handler
// still knows it, even seconds later,
// because the closure remembers it.
stopSpinner(spinner);
}
);
}
答案 1 :(得分:2)
当我有一个对象列表时,我个人不想写排序例程
通常你有一个sort函数和一个单独的函数来比较这两个对象
现在你可以在一个声明中做到这一点
List<Person> fList = new List<Person>();
fList.Sort((a, b) => a.Age.CompareTo(b.Age));
答案 2 :(得分:2)
好吧,过了一段时间,花了Scala,我无法想象一个代码,在没有闭包的情况下在某个列表上运行。例如:
val multiplier = (i:Int) => i * 2
val list1 = List(1, 2, 3, 4, 5) map multiplier
val divider = (i:Int) => i / 2
val list2 = List(1, 2, 3, 4, 5) map divider
val map = list1 zip list2
println(map)
输出为
List((2,0), (4,1), (6,1), (8,2), (10,2))
我不确定,如果是这个例子,你正在寻找,但我个人认为,闭包真正力量的最佳例子可以在列表上看到 - 例子:各种排序,搜索,迭代等
答案 3 :(得分:2)
我个人发现Stuart Langridge在Closures in JavaScript上的大量有用的演示文稿(pdf中的幻灯片)。它充满了很好的例子和一点幽默感。
答案 4 :(得分:1)
在C#中,函数可以通过靠近点来实现过滤的概念:
IEnumerable<Point> WithinRadius(this IEnumerable<Point> points, Point c, double r)
{
return points.Where(p => (p - c).Length <= r);
}
lambda(闭包)捕获提供的参数并将它们绑定到稍后将执行的计算中。
答案 5 :(得分:1)
好吧,我每天都以函数组合运算符和curry运算符的形式使用Closures,两者都是通过闭包实现的:
例如,在Scheme中使用Quicksort:
(define (qsort lst cmp)
(if (null? lst) lst
(let* ((pivot (car lst))
(stacks (split-by (cdr lst) (curry cmp pivot))))
(append (qsort (cadr stacks) cmp)
(cons pivot
(qsort (car stacks) cmp))))))
其中我cmp是一个二进制函数,通常用作(cmp一两),在这种情况下我讨论通过创建一个单一的谓词来分割我的堆栈,如果你愿意,我的咖喱运算符:
(define (curry f . args)
(lambda lst (apply f (append args lst))))
(define (curryl f . args)
(lambda lst (apply f (append lst args))))
分别是咖喱和左手。
因此,currying是闭包的一个很好的例子,另一个例子是功能组合。或者例如一个函数,它接受列表并从中生成一个谓词,该谓词测试其参数是否是该列表的成员,这也是一个闭包。
答案 6 :(得分:1)
Closure是JavaScript的强大优势之一,因为JavaScript是一种lambda语言。有关此主题的更多信息,请访问:
Trying to simplify some Javascript with closures
答案 7 :(得分:1)
好吧,说你正在生成一个菜单,比如javascript。
var menuItems = [
{ id: 1, text: 'Home' },
{ id: 2, text: 'About' },
{ id: 3, text: 'Contact' }
];
你在循环中创建它们,如下所示:
for(var i = 0; i < menuItems.length; i++) {
var menuItem = menuItems[i];
var a = document.createElement('a');
a.href = '#';
a.innerHTML = menuItem.text;
// assign onclick listener
myMenu.appendChild(a);
}
现在,对于onclick监听器,您可能会尝试这样的事情:
a.addEventListener('click', function() {
alert( menuItem.id );
}, false);
但是你会发现每个链接都会提醒'3'。因为在单击时,您的onclick代码被执行,并且menuItem
的值被计算到最后一个项目,因为这是在for循环的最后一次迭代时最后分配的值。 / p>
相反,您可以做的是为每个链接创建一个新的闭包,使用menuItem
的值,因为它在执行时的
a.addEventListener('click', (function(item) {
return function() {
alert( item.id );
}
})( menuItem ), false);
那么这里发生了什么?我们实际创建的函数接受item
,然后立即调用该函数,并传递menuItem
。因此,当您单击链接时,“包装”功能不会执行。我们立即执行它,并将返回的函数指定为点击处理程序。
这里发生的是,对于每次迭代,使用新值menuItem
调用函数,并返回一个可以访问该值的函数,这有效地创建了一个新的闭包。
希望能够解决问题=)