我问了一个关于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
总之,我们可以使用链表来存储函数定义时名称空间的状态,从而允许我们从封闭的作用域访问变量,并且能够在不影响程序其余部分的情况下对变量进行局部屏蔽。
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
封闭非常简单。我们可以这样考虑: 闭包=函数+它的词汇环境
考虑以下函数:
function init() {
var name = “Mozilla”;
}
在上述情况下,闭包是什么? 函数init()及其词法环境中的变量,即name。 闭包= init() + name
考虑另一个函数:
function init() {
var name = “Mozilla”;
function displayName(){
alert(name);
}
displayName();
}
这里的闭包是什么? 内部函数可以访问外部函数的变量。displayName()可以访问父函数init()中声明的变量名。但是,如果displayName()中存在相同的局部变量,则将使用它们。
闭包1:init函数+ (name变量+ displayName()函数)——>词法作用域
闭包2:displayName函数+(名称变量)——>词法范围
闭包是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,即其内部函数自身变量、外部函数变量和全局变量值的和。
变量作用域
声明局部变量时,该变量具有作用域。一般来说,局部变量只存在于声明它们的块或函数中。
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在内部函数的作用域内。
正常情况下,当一个函数退出时,它的所有局部变量都会消失。但是,如果我们返回inner函数并将其赋值给变量fnc,以便在outer退出后它仍然存在,那么定义inner时在作用域内的所有变量也将仍然存在。变量a已经被关闭了——它在一个闭包中。
注意变量a对于fnc是完全私有的。这是在函数式编程语言(如JavaScript)中创建私有变量的一种方式。
正如你可能猜到的那样,当我调用fnc()时,它会打印a的值,即“1”。
在没有闭包的语言中,当函数outer退出时,变量a将被垃圾收集并丢弃。调用fnc将抛出一个错误,因为a不再存在。
在JavaScript中,变量a持续存在,因为变量作用域是在函数第一次声明时创建的,并且只要函数继续存在,变量a就会持续存在。
A属于outer的范围。inner的作用域有一个父指针指向outer的作用域。FNC是一个指向内的变量。只要FNC存在,A就存在。A在闭包内。
进一步阅读(观看)
我制作了一个YouTube视频,介绍了这段代码的一些实际使用示例。