跳至正文
首页 » 博客 » What are Closures in JavaScript?

What are Closures in JavaScript?

[更新3-22-2019]

JavaScript闭包是一个记住创建它的环境的函数。我们可以将其视为具有一个方法和私有变量的对象。JavaScript闭包是一种特殊的对象,它包含函数和函数的局部作用域以及创建闭包时的所有变量 (环境)。

要理解闭包,首先我们需要理解JavaScript中的作用域。我们可以在三个范围内创建变量或函数,

  1. 全球范围
  2. 函数或局部范围
  3. 词法范围

我已经写了详细的范围在这里 ,但让我们在进入闭包之前进行一个简短的范围演练。

范围在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的词法范围。

这里有两个级别的范围:

  1. 父函数foo范围
  2. 子函数doo作用域

由于JavaScript的词法范围,在函数doo中创建的变量可以访问在函数foo范围内创建的所有内容。但是函数foo不能访问函数doo的变量。

闭包在JavaScript

让我们从一个例子开始理解JavaScript中的闭包。考虑如下所示的列表。我们不是在函数foo的主体中调用函数doo,而是从函数foo返回函数doo。

函数foo() {        var f = "foo";        函数doo() {控制台日志 (f);}        返回doo;}    var afunct = foo();afunct();

在上面的清单中:

  1. 函数foo正在返回另一个函数doo
  2. 函数doo没有任何自己的变量
  3. 由于词法范围,函数doo能够访问变量的父函数foo
  4. 函数foo被调用并分配给变量afunct
  5. 然后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