为什么JavaScript中要使用闭包?

18

我正在深入理解JavaScript闭包,目前的进展已经比较顺利了。也就是说,闭包是函数的局部变量,在函数返回后仍然保持活动状态;又或者说闭包是未被释放的堆栈帧。

我开始理解这个概念,但是越理解越想知道为什么我们要使用它们。

像这样的例子让我理解了概念,但也让我问自己,有没有更简单的方法来做到这一点!

function sayHello(name) {
   var text = 'Hello ' + name;
   var sayAlert = function() { alert(text); }
   sayAlert();
}
sayHello('Gath');

我在想为什么函数退出后,我必须让局部变量保持活动状态?

哪里可以找到使用闭包实现的解决方案示例,而且除了闭包没有其他方法可行?


7
查看此处以获取一批很棒的答案:https://dev59.com/6XVD5IYBdhLWcg3wBm5h - Nick Craver
5个回答

7
闭包为语言增添了表现力。由于闭包,可以很容易地实现一些模式。我想到的一些示例包括:

4
“闭包是穷人的对象。对象是穷人的闭包。”(抱歉,我忘记了出处。)
有时我们需要仅在一个代码块中使用的变量。我们将该代码块放入一个函数中,并将这些变量作为该函数的局部变量。
有时我们需要所有函数/代码块都需要的变量。我们可以将这些变量作为全局变量。
有时我们需要一些函数/代码块所需的变量。例如,我们有一个称为“销售”的实体,我们想将所有与销售相关的代码放在一起。因此,我们有一组处理销售的函数和一组变量。我们可以将这些函数和变量放在一个对象中,以便它们与代码的其他部分隔离开来。
在不支持对象的语言中,我们可以有嵌套函数。外部函数充当类,内部函数充当方法。对于外部函数的本地变量,它们的可访问性仅限于内部函数。在后续对内部函数的调用中,我们希望保持外部函数的局部变量的状态,这就是需要闭包的地方。我们只需通过外部函数的输出传递对任何内部函数的引用,以使外部函数访问的变量即使在外部函数退出后也能被保留。

1
我觉得我喜欢这个,但是最后一段让我有点晕...你能加些代码来解释这些类比吗? - SteveS

2

闭包是一个函数,它包含执行所需的所有环境。在JavaScript中,它是指创建一个匿名函数(= lambda),使用外部作用域中的变量。

您可以通过以下代码更好地理解:

function foo()
{
  var text = computeFromOutside();
  // ... other lines of code
  return function(otherText) { return text + otherText; }
}

bar = foo();

function baz(fun)
{
  return fun("some text");
}

在这里,您返回一个使用本地变量“text”的函数。 因此,您正在离开foo函数范围并销毁其变量。 但是,由于我们有一个使用text的匿名函数,因此我们必须跟踪此变量。 这可以通过值或引用来实现,具体取决于语言(保持变量活动(以便以后修改它,或在创建函数时复制其值))。

希望这能帮到您!


0
您选择的这个例子出现在此页面上,因此我认为您是从那里拿来的。您看过它提供的所有其他示例吗?它们比我可能解释得更好地解释了闭包的动机。

是的,我做了,但他们仍然没有展示一个情景,在那里其他所有方法都失败了,闭包是唯一能解决问题的方法。 - gath
@gath - 是的,它们确实需要。其中许多示例都是具有其他函数作为返回值的函数。如果您调用外部函数,保存对返回函数的引用,然后在稍后的某个时间点调用返回函数,那么这些示例无法在没有闭包的情况下正常工作。 - Joel Mueller
@gath:所有东西都可以用汇编语言编写。闭包并非为了解决以前无法解决的问题而发明的,因此您不会找到只能使用闭包才能解决的问题。它们使某些问题更容易解决,如果您从这个角度重新审视这些例子,它们可能会变得更加合理。 - Marcelo Cantos

0

一个普通的函数会执行它的任务,计算出结果并返回。通过返回一个闭包,你可以在工作进行到一半时将其打包,并在调用者准备好时提供更多的内容。自从我在大学尝试了Scheme之后,我一直渴望在每种编程语言中使用闭包...它们具有危险的习惯形成特性!

它们也可以说是未来的重要组成部分:连同其他函数式编程机制一起,它们似乎非常适合于多核系统的并行编程。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接