我问了一个关于curry和闭包的问题。 什么是闭包?它和咖喱有什么关系?
当前回答
闭包是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,即其内部函数自身变量、外部函数变量和全局变量值的和。
其他回答
为了帮助理解闭包,研究一下如何在过程语言中实现闭包可能会很有用。本解释将遵循Scheme中闭包的简单实现。
首先,我必须介绍名称空间的概念。在Scheme解释器中输入命令时,它必须计算表达式中的各种符号并获得它们的值。例子:
(define x 3)
(define y 4)
(+ x y) returns 7
define表达式将值3存储在x的位置,将值4存储在y的位置。然后当我们调用(+ x y)时,解释器在命名空间中查找值,并能够执行该操作并返回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,加-x为它的参数y,加上x的值。最后,在x被一个新的x遮蔽的环境中,我们调用加-x,这个x的值为5。如果我们只存储函数+ x的(+ xy)操作,因为我们在x = 5的情况下,返回的结果将是9。这就是所谓的动态作用域。
然而,Scheme、Common Lisp和许多其他语言都有所谓的词法作用域——除了存储操作(+ x y)之外,我们还将名称空间存储在特定的位置。这样,当我们查找值的时候,我们可以看到x,在这里,实际上是3。这是一个闭包。
(define x 3)
(define (plus-x y)
(+ x y))
(let ((x 5))
(plus-x 4)) returns 7
总之,我们可以使用链表来存储函数定义时名称空间的状态,从而允许我们从封闭的作用域访问变量,并且能够在不影响程序其余部分的情况下对变量进行局部屏蔽。
在正常情况下,变量受作用域规则约束:局部变量仅在定义的函数内工作。闭包是为了方便而暂时打破这一规则的一种方式。
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。
如果您来自Java世界,您可以将闭包与类的成员函数进行比较。看看这个例子
var f=function(){
var a=7;
var g=function(){
return a;
}
return g;
}
函数g是一个闭包:g封闭a。g可以和成员函数比较,a可以和类域比较,函数f可以和类比较。
闭包是一个可以引用另一个函数中的状态的函数。例如,在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
我将给出一个例子(JavaScript):
function makeCounter () {
var count = 0;
return function () {
count += 1;
return count;
}
}
var x = makeCounter();
x(); returns 1
x(); returns 2
...etc...
这个makeCounter函数的作用是,它返回一个函数,我们称之为x,每次调用它都会加1。由于我们没有为x提供任何参数,它必须以某种方式记住计数。它知道根据所谓的词法作用域在哪里找到它——它必须查找定义值的位置才能找到值。这个“隐藏”值就是所谓的闭包。
下面还是我用咖喱的例子:
function add (a) {
return function (b) {
return a + b;
}
}
var add3 = add(3);
add3(4); returns 7
您可以看到,当您使用参数a(即3)调用add时,该值包含在我们定义为add3的返回函数的闭包中。这样,当我们调用add3时,它知道在哪里找到a值来执行加法。