JavaScript闭包是一个记住创建它的环境的函数。我们可以把它想象成一个只有一个方法和私有变量的对象。JavaScript闭包是一种特殊的对象,它包含函数和函数的局部范围,以及创建闭包时的所有变量(环境)。
要理解闭包,首先我们需要理解JavaScript中的作用域。我们可以在三个层次的范围内创建变量或函数:
I have written in details about scoping here,但是在进入闭包之前,让我们先简单了解一下作用域。
一旦我们创建了一个变量,它就在全局范围内。因此,如果我们已经创建了一个不在任何函数内部的变量,它就在一个全局范围内。
var foo = "foo";
console.log(foo);
如果某样东西在全球范围内,我们可以在任何地方访问它——这使它同时成为我们的朋友和敌人。将所有东西都放在全局范围内从来都不是一个好主意,因为这可能会导致名称空间冲突和其他问题。如果某个东西在全局范围内,它可以从任何地方访问,如果函数中有同名的变量,这可能会导致冲突。
任何不在全局范围内的变量或函数都在函数或局部范围内。考虑下面的列表:
function foo() {
var doo = "doo";
console.log(doo);
}
foo();
我们创建了一个变量“doo”,它在函数“foo”的函数范围内。变量doo的生存期对于函数foo是本地的,不能在函数foo之外访问。这在JavaScript中叫做局部作用域。让我们考虑下图所示的代码:
在这里,我们已经在函数foo中创建了一个变量,与全局范围内的一个变量同名,因此现在我们有两个变量。我们要记住,这些变量是两个不同的变量,有各自的生命时间。在函数外部,值为a的变量doo是可访问的,但是在函数foo内部,值为doo的变量doo是存在的。作为参考,下面给出了上述代码:
var doo = "a";
function foo() {
var doo = "doo";
console.log(doo); //print doo
}
foo();
console.log(doo);//print a
让我们稍微调整一下上面的代码片段,如下面的清单所示:
var doo = "a";
function foo() {
doo = "doo";
console.log(doo);//print doo
}
foo();
console.log(doo);//print doo
现在我们没有变量doo的两个范围。在foo函数内部,在全局范围内创建的变量doo正在被修改。我们不是在foo中重新创建变量doo,而是从全局范围修改现有的变量doo。
在本地或函数范围内创建变量时,我们必须使用关键字var来创建变量。否则,变量将在全局范围内创建,或者如果变量已经存在于全局范围内,它将被修改。
在JavaScript中,我们可以在函数内部有一个函数。可以有任何级别的嵌套函数,这意味着我们可以有任何数量的函数相互嵌套。考虑下面的列表:
function foo() {
var f = "foo";
function doo() {
console.log(f);
}
doo();
}
foo();//print foo
本质上,在上面的代码片段中,我们有一个函数doo,它是在function foo中创建的,它没有任何自己的变量。函数foo创建了一个局部变量f,它可以在函数doo中访问。函数doo是函数foo的内部函数,它可以访问函数foo的变量。另外,函数doo可以在函数foo的体内调用。函数doo可以访问父函数中声明的变量,这是由于JavaScript的词法范围。
这里有两个层次的范围界定:
由于JavaScript的词法范围,在函数doo中创建的变量可以访问在函数foo范围内创建的所有内容。但是,function foo不能访问function doo的变量。
让我们从一个例子开始理解JavaScript中的闭包。考虑如下所示的代码。我们不是在函数foo的体内调用函数doo,而是从函数foo返回函数doo。
function foo() {
var f = "foo";
function doo() {
console.log(f);
}
return doo;
}
var afunct = foo();
afunct();
在上面的代码中:
令人惊讶的是,上述代码片段的输出是字符串“foo”。现在我们可能会感到困惑——变量f是如何在函数foo之外被访问的?通常,函数中的局部变量只在函数执行期间存在。因此,理想情况下,在执行foo之后,变量f应该不再是可访问的。但是在JavaScript中,我们可以访问它,因为afunct已经变成了JavaScript闭包。闭包afunct在afunct闭包创建时拥有关于函数doo和函数doo的所有局部范围变量的信息。
在闭包的情况下,内部函数保留外部函数范围的引用。所以,在闭包中:
为了更好地理解闭包,让我们再讨论一个例子:
function add(num1) {
function addintern(num2) {
return num1 + num2;
} return addintern;
}
var sum9 = add(7)(2);
console.log(sum9);
var sum99 = add(77)(22);
console.log(sum99);
我们这里有两个闭包,sum9和sum99。
当创建sum9闭包时,在函数addintern的局部范围内,num1的值是7,JavaScript在创建sum9闭包时会记住这个值。
关闭sum99时也是如此...在函数addintern的局部范围内,num1的值是7,JavaScript在创建闭包sum99时记住了这个值。正如预期的那样,产出将是9和99。
我们可以把闭包想象成一个带有私有变量和一个方法的对象。闭包允许我们用对数据起作用的函数来附加数据。因此,闭包可以定义为具有以下特征:
最后,我们可以定义一个闭包:
“JavaScript闭包是一种特殊的对象,它包含一个函数和创建该函数的环境。这里,环境代表函数的局部范围及其在闭包创建时的所有变量。”
在这篇文章中,我们学习了JavaScript中的闭包。希望你觉得有用。感谢阅读!