我问了一个关于curry和闭包的问题。 什么是闭包?它和咖喱有什么关系?
当前回答
下面是另一个现实生活中的例子,使用了游戏中流行的脚本语言——Lua。我需要稍微改变一个库函数的工作方式,以避免stdin不可用的问题。
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函数可以访问它,或者更确切地说,作为“upvalue”与函数一起存储的副本。
其他回答
请看看下面的代码,以更深入地理解闭包:
for(var i=0; i< 5; i++){
setTimeout(function(){
console.log(i);
}, 1000);
}
这里会输出什么?0 1 2 3 4不是5 5 5 5 5 5 5 5 5因为是闭包的
那么它将如何解决呢?答案如下:
for(var i=0; i< 5; i++){
(function(j){ //using IIFE
setTimeout(function(){
console.log(j);
},1000);
})(i);
}
让我简单解释一下,当一个函数创建时,直到它在第一个代码中调用5次for循环才会发生,但不会立即调用所以当它在1秒后调用i.e时,这也是异步的,所以在这个for循环结束之前,将值5存储在var i中最后执行5次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特性),但在引子下面,上面的函数是工作的 (让我= 0;我< 5;我+ +){ setTimeout(函数(){ console.log(我); }, 1000); } 输出: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如上所述。
博士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的含义:您使用函数(闭包)来传递操作的每个参数,而不是使用带有多个参数的一个函数。
在正常情况下,变量受作用域规则约束:局部变量仅在定义的函数内工作。闭包是为了方便而暂时打破这一规则的一种方式。
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。
闭包是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编程语言中的闭包。
首先定义一个函数,该函数定义了一个局部变量,在函数外部不可见。
; 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