我问了一个关于 Currying 的问题,并提到了闭包。什么是闭包?它与咖喱有什么关系?
24 回答
变量范围
当您声明一个局部变量时,该变量具有一个范围。通常,局部变量只存在于声明它们的块或函数中。
function() {
var a = 1;
console.log(a); // works
}
console.log(a); // fails
如果我尝试访问一个局部变量,大多数语言会在当前作用域中查找它,然后通过父作用域向上查找,直到它们到达根作用域。
var a = 1;
function() {
console.log(a); // works
}
console.log(a); // works
当一个块或函数完成后,它的局部变量就不再需要了,而且通常会耗尽内存。
这就是我们通常期望的工作方式。
闭包是一个持久的局部变量范围
闭包是一个持久作用域,即使在代码执行移出该块之后,它也会保留局部变量。支持闭包的语言(例如 JavaScript、Swift 和 Ruby)将允许您保留对范围(包括其父范围)的引用,即使在声明这些变量的块已完成执行之后,只要您保留引用到某个地方的那个块或功能。
范围对象及其所有局部变量都与函数相关联,只要该函数存在,它就会持续存在。
这给了我们功能的可移植性。即使我们在完全不同的上下文中调用函数,我们也可以预期,当函数第一次定义时在作用域内的任何变量在以后调用函数时仍然在作用域内。
例如
这是一个非常简单的 JavaScript 示例,说明了这一点:
outer = function() {
var a = 1;
var inner = function() {
console.log(a);
}
return inner; // this returns a function
}
var fnc = outer(); // execute outer to get inner
fnc();
在这里,我在函数中定义了一个函数。内部函数可以访问所有外部函数的局部变量,包括a
. 该变量a
在内部函数的范围内。
通常,当一个函数退出时,它的所有局部变量都会被吹走。但是,如果我们返回内部函数并将其分配给一个变量fnc
,以便它在outer
退出后仍然存在,那么在定义时在范围内的所有变量也都inner
将持续存在。变量a
已被关闭——它在一个闭包内。
请注意,该变量a
是完全私有的fnc
。这是一种在函数式编程语言(如 JavaScript)中创建私有变量的方法。
正如您可能猜到的,当我调用fnc()
它时会打印 的值a
,即“1”。
在没有闭包的语言中,该变量将在函数退出a
时被垃圾收集并丢弃。outer
调用 fnc 会引发错误,因为a
不再存在。
在 JavaScript 中,变量a
保持不变是因为变量作用域是在函数第一次声明时创建的,并且只要函数继续存在就保持不变。
a
属于范围outer
。的范围inner
有一个指向范围的父指针outer
。fnc
是一个指向 的变量inner
。a
只要坚持就fnc
坚持。a
是在封闭内。
进一步阅读(观看)
我制作了一个YouTube 视频,通过一些实际的使用示例查看此代码。
我将举一个例子(在 JavaScript 中):
function makeCounter () {
var count = 0;
return function () {
count += 1;
return count;
}
}
var x = makeCounter();
x(); returns 1
x(); returns 2
...etc...
这个函数 makeCounter 所做的是它返回一个我们称之为 x 的函数,每次调用它都会加一。由于我们没有为 x 提供任何参数,它必须以某种方式记住计数。它根据所谓的词法作用域知道在哪里找到它——它必须查看它定义的位置以找到值。这个“隐藏”值就是所谓的闭包。
这是我的柯里化示例:
function add (a) {
return function (b) {
return a + b;
}
}
var add3 = add(3);
add3(4); returns 7
您可以看到,当您使用参数 a(即 3)调用 add 时,该值包含在我们定义为 add3 的返回函数的闭包中。这样,当我们调用 add3 时,它知道在哪里可以找到执行加法的 a 值。
首先,与这里大多数人告诉你的相反,闭包不是函数!那是什么?
它是在函数的“周围上下文”(称为其环境)中定义的一组符号,使其成为 CLOSED 表达式(即,每个符号都被定义并具有值的表达式,因此可以对其进行评估)。
例如,当您有一个 JavaScript 函数时:
function closed(x) {
return x + 3;
}
它是一个封闭表达式,因为其中出现的所有符号都在其中定义(它们的含义很清楚),因此您可以对其进行评估。换句话说,它是自包含的。
但是如果你有这样的功能:
function open(x) {
return x*y + 3;
}
它是一个开放的表达式,因为其中有一些尚未在其中定义的符号。即,y
。在查看这个函数时,我们不知道它是什么y
以及它是什么意思,我们不知道它的值,所以我们无法评估这个表达式。y
即,在我们告诉它应该是什么意思之前,我们不能调用这个函数。这y
称为自由变量。
这y
需要一个定义,但这个定义不是函数的一部分——它是在其他地方定义的,在它的“周围环境”(也称为环境)中。至少这是我们希望的:P
例如,它可以在全局范围内定义:
var y = 7;
function open(x) {
return x*y + 3;
}
或者它可以在包装它的函数中定义:
var global = 2;
function wrapper(y) {
var w = "unused";
return function(x) {
return x*y + 3;
}
}
为表达式中的自由变量赋予含义的环境部分是闭包。之所以这样称呼它,是因为它通过为其所有自由变量提供这些缺失的定义,将一个开放的表达式变成一个封闭的表达式,这样我们就可以评估它。
在上面的示例中,内部函数(我们没有给出名称,因为我们不需要它)是一个开放表达式,因为其中的变量y
是自由的——它的定义在函数之外,在包装它的函数中. 该匿名函数的环境是一组变量:
{
global: 2,
w: "unused",
y: [whatever has been passed to that wrapper function as its parameter `y`]
}
现在,闭包是这个环境的一部分,它通过提供所有自由变量的定义来关闭内部函数。在我们的例子中,内部函数中唯一的自由变量是,因此该函数的闭包是其环境的这个子集:y
{
y: [whatever has been passed to that wrapper function as its parameter `y`]
}
环境中定义的其他两个符号不是该函数闭包的一部分,因为它不需要它们运行。他们不需要关闭它。
更多关于这里背后的理论: https ://stackoverflow.com/a/36878651/434562
值得注意的是,在上面的示例中,包装函数将其内部函数作为值返回。从定义(或创建)函数的那一刻起,我们调用此函数的那一刻就可能是远程的。尤其是它的包装函数不再运行,它的在调用栈上的参数也不再存在了 :P 这造成了一个问题,因为内部函数y
在被调用时需要在那里!换句话说,它要求闭包中的变量以某种方式比包装函数更长寿,并在需要时出现。因此,内部函数必须对这些变量进行快照,使其关闭并将它们存储在安全的地方以备后用。(在调用堆栈之外的某个地方。)
这就是为什么人们经常将闭包这个术语混淆为一种特殊类型的函数,它可以对他们使用的外部变量或用于以后存储这些变量的数据结构进行快照。但我希望你现在明白它们不是闭包本身——它们只是在编程语言中实现闭包的方法,或者是允许函数闭包中的变量在需要时出现的语言机制。关于闭包有很多误解,这(不必要地)使这个主题比实际情况更加混乱和复杂。
凯尔的回答很好。我认为唯一的额外说明是闭包基本上是创建 lambda 函数时堆栈的快照。然后,当重新执行函数时,堆栈会恢复到执行函数之前的状态。因此,正如 Kyle 所提到的,count
当 lambda 函数执行时,隐藏的值 ( ) 是可用的。
闭包是一个可以在另一个函数中引用状态的函数。例如,在 Python 中,它使用闭包“inner”:
def outer (a):
b = "variable in outer()"
def inner (c):
print a, b, c
return inner
# Now the return value from outer() can be saved for later
func = outer ("test")
func (1) # prints "test variable in outer() 1
为了帮助理解闭包,检查它们如何在过程语言中实现可能会很有用。这个解释将遵循 Scheme 中闭包的简单实现。
首先,我必须介绍命名空间的概念。当您将命令输入到 Scheme 解释器中时,它必须计算表达式中的各种符号并获取它们的值。例子:
(define x 3)
(define y 4)
(+ x y) returns 7
定义表达式将值 3 存储在 x 的位置,将值 4 存储在 y 的位置。然后当我们调用 (+ xy) 时,解释器在命名空间中查找值并能够执行操作并返回 7。
但是,在 Scheme 中,有一些表达式允许您临时覆盖符号的值。这是一个例子:
(define x 3)
(define y 4)
(let ((x 5))
(+ x y)) returns 9
x returns 3
let 关键字的作用是引入一个新的命名空间,其中 x 的值为 5。您会注意到它仍然可以看到 y 为 4,因此返回的总和为 9。您还可以看到,一旦表达式结束 x又回到了 3。从这个意义上说,x 暂时被局部值掩盖了。
过程和面向对象的语言具有类似的概念。每当您在函数中声明与全局变量同名的变量时,您都会获得相同的效果。
我们将如何实现这一点?一种简单的方法是使用链表——头部包含新值,尾部包含旧命名空间。当你需要查找一个符号时,你从头部开始,沿着尾部向下工作。
现在让我们暂时跳到一流函数的实现。或多或少,函数是一组指令,当函数被调用并最终返回值时要执行。当我们读入一个函数时,我们可以将这些指令存储在幕后,并在函数被调用时运行它们。
(define x 3)
(define (plus-x y)
(+ x y))
(let ((x 5))
(plus-x 4)) returns ?
我们将 x 定义为 3,将 plus-x 定义为其参数 y 加上 x 的值。最后,我们在 x 被新 x 屏蔽的环境中调用 plus-x,这个 x 的值为 5。如果我们只存储函数 plus-x 的操作 (+ xy),因为我们在上下文中如果 x 为 5,则返回的结果将为 9。这就是所谓的动态作用域。
然而,Scheme、Common Lisp 和许多其他语言都有所谓的词法作用域——除了存储操作 (+ xy) 之外,我们还在该特定点存储命名空间。这样,当我们查找值时,我们可以看到 x 在这种情况下实际上是 3。这是一个闭包。
(define x 3)
(define (plus-x y)
(+ x y))
(let ((x 5))
(plus-x 4)) returns 7
总之,我们可以使用链表来存储函数定义时命名空间的状态,允许我们从封闭范围访问变量,并为我们提供本地屏蔽变量而不影响其余部分的能力程序。
不包含自由变量的函数称为纯函数。
包含一个或多个自由变量的函数称为闭包。
var pure = function pure(x){
return x
// only own environment is used
}
var foo = "bar"
var closure = function closure(){
return foo
// foo is a free variable from the outer environment
}
这是一个真实世界的例子,说明为什么 Closures 很糟糕……这直接来自我的 Javascript 代码。让我来说明一下。
Function.prototype.delay = function(ms /*[, arg...]*/) {
var fn = this,
args = Array.prototype.slice.call(arguments, 1);
return window.setTimeout(function() {
return fn.apply(fn, args);
}, ms);
};
以下是您将如何使用它:
var startPlayback = function(track) {
Player.play(track);
};
startPlayback(someTrack);
现在假设您希望延迟开始播放,例如在此代码片段运行后 5 秒。嗯,这很容易,delay
而且它的关闭:
startPlayback.delay(5000, someTrack);
// Keep going, do other things
当您delay
使用5000
ms 调用时,第一个片段运行,并将传入的参数存储在它的闭包中。然后 5 秒后,当setTimeout
回调发生时,闭包仍然维护那些变量,所以它可以使用原始参数调用原始函数。
这是一种咖喱,或功能装饰。
如果没有闭包,您将不得不以某种方式在函数外部维护这些变量状态,从而在函数外部乱扔代码,其中包含逻辑上属于内部的东西。使用闭包可以极大地提高代码的质量和可读性。
tl;博士
闭包是一个函数,它的作用域分配给(或用作)一个变量。因此,名称闭包:作用域和函数像任何其他实体一样被封闭和使用。
深入的维基百科风格解释
在具有一流功能的语言中实现词法范围名称绑定的技术。
这意味着什么?让我们看看一些定义。
我将使用这个例子来解释闭包和其他相关定义:
function startAt(x) {
return function (y) {
return x + y;
}
}
var closure1 = startAt(1);
var closure2 = startAt(5);
console.log(closure1(3)); // 4 (x == 1, y == 3)
console.log(closure2(3)); // 8 (x == 5, y == 3)
一流的功能
基本上这意味着我们可以像使用任何其他实体一样使用函数。我们可以修改它们,将它们作为参数传递,从函数返回它们或将它们分配给变量。从技术上讲,它们是一等公民,因此得名:一等函数。
在上面的示例中,startAt
返回一个(匿名)函数,该函数被分配给closure1
和closure2
。因此,正如您所见,JavaScript 将函数视为与任何其他实体(一等公民)一样。
名称绑定
名称绑定是关于找出变量(标识符)引用的数据。范围在这里非常重要,因为它将决定如何解决绑定。
在上面的例子中:
- 在内部匿名函数的范围内,
y
绑定到3
. - 在
startAt
's 范围内,x
绑定到1
or5
(取决于闭包)。
在匿名函数的作用域内,x
没有绑定任何值,因此需要在上层 ( startAt
's) 作用域中解析。
词法作用域
正如维基百科所说,范围:
是绑定有效的计算机程序区域:其中名称可用于引用实体。
有两种技术:
- 词法(静态)作用域:变量的定义通过搜索其包含块或函数来解析,然后如果搜索外部包含块失败,依此类推。
- 动态范围:调用函数被搜索,然后是调用该调用函数的函数,依此类推,向上调用堆栈。
有关更多解释,请查看此问题并查看 Wikipedia。
在上面的例子中,我们可以看到 JavaScript 是词法作用域的,因为当x
解析时,绑定会在上层 ( startAt
's) 作用域中搜索,基于源代码(查找 x 的匿名函数在内部定义startAt
)和不基于调用堆栈,调用函数的方式(范围)。
包裹(关闭)
在我们的示例中,当我们调用 时startAt
,它将返回一个(一等)函数,该函数将被分配给closure1
并closure2
因此创建一个闭包,因为传递的变量1
将5
保存在startAt
的范围内,这将与返回的匿名函数。当我们通过closure1
和closure2
使用相同的参数 ( 3
)调用此匿名函数时,y
会立即找到x
(词法上)上层函数作用域(保存在闭包中)x
被发现绑定到1
或5
. 现在我们知道了求和的所有内容,因此可以返回结果,然后打印。
现在您应该了解闭包及其行为方式,这是 JavaScript 的基本部分。
咖喱
哦,您还了解了柯里化的含义:您使用函数(闭包)来传递操作的每个参数,而不是使用具有多个参数的函数。
闭包是 JavaScript 中的一项功能,其中函数可以访问自己的作用域变量、访问外部函数变量和访问全局变量。
即使在外部函数返回后,闭包也可以访问其外部函数范围。这意味着即使在函数完成后,闭包也可以记住和访问其外部函数的变量和参数。
内部函数可以访问在它自己的作用域、外部函数的作用域和全局作用域中定义的变量。并且外部函数可以访问在自己的范围和全局范围内定义的变量。
闭包示例:
var globalValue = 5;
function functOuter() {
var outerFunctionValue = 10;
//Inner function has access to the outer function value
//and the global variables
function functInner() {
var innerFunctionValue = 5;
alert(globalValue + outerFunctionValue + innerFunctionValue);
}
functInner();
}
functOuter();
输出将是 20,即其内部函数自身变量、外部函数变量和全局变量值的总和。
在正常情况下,变量受范围规则约束:局部变量仅在定义的函数内起作用。为方便起见,关闭是一种暂时打破此规则的方法。
def n_times(a_thing)
return lambda{|n| a_thing * n}
end
在上面的代码中,lambda(|n| a_thing * n}
是闭包,因为a_thing
它被 lambda(匿名函数创建者)引用。
现在,如果您将生成的匿名函数放入函数变量中。
foo = n_times(4)
foo 将打破正常的范围规则并开始在内部使用 4。
foo.call(3)
返回 12。
闭包是由另一个函数返回的有状态函数。即使父函数已完成执行,它也充当容器来记住其父作用域中的变量和参数。考虑这个简单的例子。
function sayHello() {
const greeting = "Hello World";
return function() { // anonymous function/nameless function
console.log(greeting)
}
}
const hello = sayHello(); // hello holds the returned function
hello(); // -> Hello World
看!我们有一个返回函数的函数!返回的函数被保存到一个变量并调用下面的行。
简而言之,函数指针只是指向程序代码库中某个位置的指针(如程序计数器)。而闭包 = 函数指针 + 堆栈帧。
.
闭包为 JavaScript 提供状态。
编程中的状态仅仅意味着记住事物。
例子
var a = 0;
a = a + 1; // => 1
a = a + 1; // => 2
a = a + 1; // => 3
在上述情况下,状态存储在变量“a”中。我们随后将“a”数次加 1。我们只能这样做,因为我们能够“记住”该值。状态持有者“a”将该值保存在内存中。
通常,在编程语言中,您希望跟踪事物、记住信息并在以后访问它。
在其他语言中,这通常是通过使用类来实现的。一个类,就像变量一样,跟踪它的状态。反过来,该类的实例也具有其中的状态。状态只是意味着您可以在以后存储和检索的信息。
例子
class Bread {
constructor (weight) {
this.weight = weight;
}
render () {
return `My weight is ${this.weight}!`;
}
}
我们如何从“渲染”方法中访问“权重”?好吧,感谢国家。Bread 类的每个实例都可以通过从“状态”读取它来呈现自己的权重,“状态”是我们可以存储该信息的内存位置。
现在,JavaScript 是一种非常独特的语言,它在历史上没有类(现在有,但在底层只有函数和变量),因此闭包为 JavaScript 提供了一种记住事物并在以后访问它们的方法。
例子
var n = 0;
var count = function () {
n = n + 1;
return n;
};
count(); // # 1
count(); // # 2
count(); // # 3
上面的例子用一个变量实现了“保持状态”的目标。这很棒!然而,这有一个缺点,即变量(“状态”持有者)现在暴露了。我们可以做得更好。我们可以使用闭包。
例子
var countGenerator = function () {
var n = 0;
var count = function () {
n = n + 1;
return n;
};
return count;
};
var count = countGenerator();
count(); // # 1
count(); // # 2
count(); // # 3
这是太棒了。
现在我们的“计数”函数可以计数了。它之所以能够这样做,是因为它可以“保持”状态。这种情况下的状态是变量“n”。此变量现已关闭。在时间和空间上封闭。及时,因为您将永远无法恢复、更改、分配值或直接与其交互。在空间中,因为它在地理上嵌套在“countGenerator”函数中。
为什么这太棒了?因为在不涉及任何其他复杂的工具(例如类、方法、实例等)的情况下,我们能够 1. 隐藏 2. 远距离控制
我们隐藏了状态变量“n”,使其成为私有变量!我们还创建了一个 API,可以以预定义的方式控制此变量。特别是,我们可以像“count()”这样调用 API,并将“距离”的“n”加 1。除非通过 API,否则任何人都无法访问“n”。
JavaScript 的简单性确实令人惊叹。
闭包是为什么会这样的重要部分。
• 闭包是一个子程序和定义它的引用环境
– 如果可以从程序中的任意位置调用子程序,则需要引用环境
– 不允许嵌套子程序的静态范围语言不需要闭包
– 仅当子程序可以访问嵌套范围内的变量并且可以从任何地方调用时才需要闭包
– 为了支持闭包,实现可能需要为某些变量提供无限范围(因为子程序可能会访问通常不再存在的非局部变量)
例子
function makeAdder(x) {
return function(y) {return x + y;}
}
var add10 = makeAdder(10);
var add5 = makeAdder(5);
document.write(″add 10 to 20: ″ + add10(20) +
″<br />″);
document.write(″add 5 to 20: ″ + add5(20) +
″<br />″);
这是另一个现实生活中的例子,它使用了游戏中流行的脚本语言——Lua。我需要稍微改变库函数的工作方式以避免标准输入不可用的问题。
local old_dofile = dofile
function dofile( filename )
if filename == nil then
error( 'Can not use default of stdin.' )
end
old_dofile( filename )
end
old_dofile 的值在此代码块完成其范围时消失(因为它是本地的),但是该值已包含在闭包中,因此新的重新定义的 dofile 函数可以访问它,或者更确切地说是与函数一起存储的副本作为'升值'。
来自Lua.org:
当一个函数被写成封闭在另一个函数中时,它可以完全访问封闭函数的局部变量;这个特性称为词法作用域。虽然这听起来很明显,但事实并非如此。词法作用域加上一流的函数是编程语言中的一个强大概念,但很少有语言支持该概念。
如果您来自 Java 世界,您可以将闭包与类的成员函数进行比较。看这个例子
var f=function(){
var a=7;
var g=function(){
return a;
}
return g;
}
函数g
是闭包:g
closes a
in。所以g
可以和成员函数a
比较,可以和类字段比较,函数可以和f
类比较。
闭包 每当我们在另一个函数内部定义了一个函数时,内部函数就可以访问外部函数中声明的变量。闭包最好用例子来解释。在清单 2-18 中,您可以看到内部函数可以从外部范围访问变量(variableInOuterFunction)。外部函数中的变量已被内部函数封闭(或绑定)。因此,术语关闭。这个概念本身很简单,也相当直观。
Listing 2-18:
function outerFunction(arg) {
var variableInOuterFunction = arg;
function bar() {
console.log(variableInOuterFunction); // Access a variable from the outer scope
}
// Call the local function to demonstrate that it has access to arg
bar();
}
outerFunction('hello closure!'); // logs hello closure!
来源: http: //index-of.es/Varios/Basarat%20Ali%20Syed%20 (auth.)-Beginning%20Node.js-Apress%20(2014).pdf
请查看下面的代码以更深入地了解闭包:
for(var i=0; i< 5; i++){
setTimeout(function(){
console.log(i);
}, 1000);
}
这里会输出什么?0,1,2,3,4
不是5,5,5,5,5
因为关闭
那么它将如何解决呢?答案如下:
for(var i=0; i< 5; i++){
(function(j){ //using IIFE
setTimeout(function(){
console.log(j);
},1000);
})(i);
}
让我简单解释一下,当一个函数创建之前什么都没有发生,直到它在第一个代码中调用了 for 循环,调用了 5 次但没有立即调用,所以当它调用时,即 1 秒后,而且这是异步的,所以在这个 for 循环完成并存储值 5 之前在 var i 中,最后执行setTimeout
函数五次并打印5,5,5,5,5
这里是如何使用 IIFE 解决的,即立即调用函数表达式
(function(j){ //i is passed here
setTimeout(function(){
console.log(j);
},1000);
})(i); //look here it called immediate that is store i=0 for 1st loop, i=1 for 2nd loop, and so on and print 0,1,2,3,4
有关更多信息,请了解执行上下文以了解关闭。
还有另一种解决方案可以使用 let(ES6 功能)解决此问题,但在引擎盖下,上面的功能是有效的
for(let i=0; i< 5; i++){ setTimeout(function(){ console.log(i); },1000); } Output: 0,1,2,3,4
=> 更多解释:
在内存中,当 for 循环执行图片时,如下所示:
循环 1)
setTimeout(function(){
console.log(i);
},1000);
循环 2)
setTimeout(function(){
console.log(i);
},1000);
循环 3)
setTimeout(function(){
console.log(i);
},1000);
循环 4)
setTimeout(function(){
console.log(i);
},1000);
循环 5)
setTimeout(function(){
console.log(i);
},1000);
这里 i 没有执行,然后在完成循环后, var i 将值 5 存储在内存中,但它的范围在它的子函数中始终可见,因此当函数从内setTimeout
向外执行五次时,它会打印5,5,5,5,5
因此要解决此问题,请使用 IIFE,如上所述。
Currying :它允许您通过仅传入其参数的子集来部分评估函数。考虑一下:
function multiply (x, y) {
return x * y;
}
const double = multiply.bind(null, 2);
const eight = double(4);
eight == 8;
闭包:闭包只不过是访问函数范围之外的变量。重要的是要记住,函数内部的函数或嵌套函数不是闭包。当需要访问函数范围之外的变量时,总是使用闭包。
function apple(x){
function google(y,z) {
console.log(x*y);
}
google(7,2);
}
apple(3);
// the answer here will be 21
关闭非常容易。我们可以这样考虑:闭包=函数+它的词法环境
考虑以下函数:
function init() {
var name = “Mozilla”;
}
在上述情况下将关闭什么?函数 init() 及其词法环境中的变量,即名称。 闭包= init() + 名称
考虑另一个功能:
function init() {
var name = “Mozilla”;
function displayName(){
alert(name);
}
displayName();
}
这里将关闭什么?内部函数可以访问外部函数的变量。displayName() 可以访问在父函数 init() 中声明的变量名。但是,如果存在,将使用 displayName() 中相同的局部变量。
闭包 1:初始化函数 +(名称变量 + displayName() 函数)--> 词法范围
闭包 2 : displayName function + (name variable) --> lexical scope
Groovy 中的一个简单示例供您参考:
def outer() {
def x = 1
return { -> println(x)} // inner
}
def innerObj = outer()
innerObj() // prints 1
这是一个说明 Scheme 编程语言中的闭包的示例。
首先我们定义一个函数,定义一个局部变量,在函数外不可见。
; Function using a local variable
(define (function)
(define a 1)
(display a) ; prints 1, when calling (function)
)
(function) ; prints 1
(display a) ; fails: a undefined
这是相同的示例,但现在该函数使用了一个在函数外部定义的全局变量。
; Function using a global variable
(define b 2)
(define (function)
(display b) ; prints 2, when calling (function)
)
(function) ; prints 2
(display 2) ; prints 2
最后,这是一个带有自己闭包的函数的示例:
; Function with closure
(define (outer)
(define c 3)
(define (inner)
(display c))
inner ; outer function returns the inner function as result
)
(define function (outer))
(function) ; prints 3