我问了一个关于curry和闭包的问题。 什么是闭包?它和咖喱有什么关系?
当前回答
在正常情况下,变量受作用域规则约束:局部变量仅在定义的函数内工作。闭包是为了方便而暂时打破这一规则的一种方式。
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。
其他回答
下面的示例演示了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
在正常情况下,变量受作用域规则约束:局部变量仅在定义的函数内工作。闭包是为了方便而暂时打破这一规则的一种方式。
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。
博士tl;
闭包是一个函数,它的作用域赋给(或用作)一个变量。因此,名称闭包:作用域和函数被封闭起来,并像其他实体一样使用。
维基百科式的深度解释
根据维基百科,闭包是:
在具有一流函数的语言中实现词法作用域名称绑定的技术。
这是什么意思?让我们来看看一些定义。
我将通过下面的例子解释闭包和其他相关定义:
函数startAt(x) { 返回函数(y) { 返回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的作用域中,x被绑定为1或5(取决于闭包)。
在匿名函数的作用域中,x没有绑定到任何值,因此需要在upper (startAt)作用域中解析它。
词法作用域
正如维基百科所说,范围:
是计算机程序中绑定有效的区域:其中的名称可用于引用实体。
有两种方法:
词法(静态)作用域:通过搜索包含变量的块或函数来解析变量的定义,如果搜索外部包含块失败,依此类推。 动态作用域:先搜索调用函数,然后搜索调用该函数的函数,依此类推,在调用堆栈中向上移动。
更多的解释,看看这个问题,看看维基百科。
在上面的例子中,我们可以看到JavaScript是词法范围的,因为当解析x时,绑定是在上层(startAt的)范围内搜索的,基于源代码(寻找x的匿名函数是在startAt内部定义的),而不是基于调用堆栈,函数被调用的方式(范围)。
裹(合)起来
In our example, when we call startAt, it will return a (first-class) function that will be assigned to closure1 and closure2 thus a closure is created, because the passed variables 1 and 5 will be saved within startAt's scope, that will be enclosed with the returned anonymous function. When we call this anonymous function via closure1 and closure2 with the same argument (3), the value of y will be found immediately (as that is the parameter of that function), but x is not bound in the scope of the anonymous function, so the resolution continues in the (lexically) upper function scope (that was saved in the closure) where x is found to be bound to either 1 or 5. Now we know everything for the summation so the result can be returned, then printed.
现在您应该了解闭包及其行为,这是JavaScript的基本部分。
局部套用
哦,您还了解了curry的含义:您使用函数(闭包)来传递操作的每个参数,而不是使用带有多个参数的一个函数。
凯尔的回答很好。我认为唯一需要澄清的是,闭包基本上是lambda函数创建时堆栈的快照。然后,当函数重新执行时,堆栈将恢复到执行函数之前的状态。因此,正如Kyle提到的,当lambda函数执行时,隐藏值(count)是可用的。
curry:它允许你通过只传入函数参数的子集来部分求值。考虑一下:
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