[更新3-22-2019]
JavaScript闭包是一个记住创建它的环境的函数。我们可以将其视为具有一个方法和私有变量的对象。JavaScript闭包是一种特殊的对象,它包含函数和函数的局部作用域以及创建闭包时的所有变量 (环境)。
要理解闭包,首先我们需要理解JavaScript中的作用域。我们可以在三个范围内创建变量或函数,
- 全球范围
- 函数或局部范围
- 词法范围
我已经写了详细的范围在这里 ,但让我们在进入闭包之前进行一个简短的范围演练。
范围在JavaScript
一旦我们创建了一个变量,它就在一个全局范围内。所以,如果我们创建了一个不在任何函数内部的变量,它就在一个全局范围内。
var foo = "foo";console.log(foo);
如果某个东西在全球范围内,我们可以在任何地方访问它-这使它同时成为我们的朋友和敌人。将所有内容放在全局范围内从来都不是一个好主意,因为它可能会导致其他问题之间的命名空间冲突。如果某个东西在全局范围内,它可以从任何地方访问,如果在函数中命名相同的变量,这可能会导致冲突。
任何不在全局范围内的变量或函数都在函数或局部范围内。考虑下面的列表:
函数foo() { var doo = "doo";console.log(doo);}foo();
我们创建了一个变量doo,它位于函数foo的函数范围内。变量doo的生命周期是本地函数foo,它不能在该函数之外访问。这在JavaScript中称为本地作用域。让我们考虑如下图所示的代码清单:
这里我们在函数foo中创建了一个变量,其名称与全局范围内的变量相同,因此现在我们有两个变量。我们应该记住,这些变量是两个不同的变量,它们各自的寿命。在函数外部,具有值a的变量doo是可访问的,但是在函数foo内部,具有vale doo的变量doo存在。作为参考,上面的代码如下:
var doo = "a"; 函数foo() { var doo = "doo";// 打印doo}foo();// 打印一个
让我们稍微调整一下上面的代码片段,如下面的清单所示:
var doo = "a"; 函数foo() {doo = "doo";console.log(doo) // 打印doo}foo();// 打印doo
现在我们没有变量doo的两个作用域。在函数foo中,在全局范围内创建的变量doo正在被修改。我们不是在foo中重新创建变量doo,而是从全局范围修改现有的变量doo。
在本地或函数作用域中创建变量时,我们必须使用关键字var来创建变量。否则,将在全局范围内创建变量,或者如果变量已经存在于全局范围内,则将对其进行修改。
在JavaScript中,我们可以在函数内部有一个函数。可以有任何级别的嵌套函数,这意味着我们可以有任意数量的函数嵌套在彼此之间。考虑下面的列表:
函数foo() { varf= "foo"; 函数doo() {控制台日志 (f);}doo();}// 打印foo
基本上在上面的代码片段中,我们有一个在function foo中创建的函数doo,它没有任何自己的变量。函数foo创建一个局部变量,它可以在函数doo中访问。函数doo是函数foo的内部函数,它可以访问函数foo的变量。此外,函数doo可以在函数foo的体内调用。函数doo可以访问父函数中声明的变量,这是由于JavaScript的词法范围。
这里有两个级别的范围:
- 父函数foo范围
- 子函数doo作用域
由于JavaScript的词法范围,在函数doo中创建的变量可以访问在函数foo范围内创建的所有内容。但是函数foo不能访问函数doo的变量。
闭包在JavaScript
让我们从一个例子开始理解JavaScript中的闭包。考虑如下所示的列表。我们不是在函数foo的主体中调用函数doo,而是从函数foo返回函数doo。
函数foo() { var f = "foo"; 函数doo() {控制台日志 (f);} 返回doo;} var afunct = foo();afunct();
在上面的清单中:
- 函数foo正在返回另一个函数doo
- 函数doo没有任何自己的变量
- 由于词法范围,函数doo能够访问变量的父函数foo
- 函数foo被调用并分配给变量afunct
- 然后afunct被称为一个函数,它打印字符串 “foo”
令人惊讶的是,上面的代码片段的输出是字符串 “foo”。现在我们可能会感到困惑: 如何在函数foo之外访问变量f?通常,函数中的局部变量仅在该函数执行期间存在。因此,理想情况下,在执行foo之后,变量f应该不再可访问。但是在JavaScript中我们可以访问它,因为afunct已经成为一个JavaScript闭包。在创建afunct闭包时,闭包afunct具有有关函数doo和函数doo的所有局部作用域变量的信息。
在闭包的情况下,内部函数保留外部函数范围的引用。所以,在闭包中:
- 内部函数保持对其外部函数范围的引用。在这种情况下,函数doo保留函数foo范围的引用。
- 函数doo可以随时从函数食物范围引用访问变量,即使外部函数foo完成执行。
- JavaScript将外部函数 (在这种情况下为foo) 的作用域引用及其变量保留在内存中,直到内部函数存在并引用它。在这种情况下,函数foo的作用域和变量将由JavaScript保存在内存中,直到函数doo存在。
为了更好地理解闭包,让我们再讨论一个例子:
函数add(num1) { 函数addintern(num2) { 返回num1 num2;} 返回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闭包是一种特殊的对象,它包含一个函数和创建函数的环境。在这里,environment代表函数的局部范围及其在闭包创建时的所有变量。
结论
在这篇文章中,我们学习了JavaScript中的闭包。我希望你觉得它有用。感谢您的阅读!</p