JavaScript - 闭包



什么是闭包?

JavaScript 中的闭包概念允许嵌套函数访问父函数作用域中定义的变量,即使父函数的执行已经结束。简而言之,您可以使用闭包将全局变量变为局部变量或私有变量。

JavaScript 闭包基本上是函数及其词法环境的组合。这允许内部函数访问外部函数的作用域。闭包在函数创建时被创建。

在开始学习闭包概念之前,您需要了解词法作用域、嵌套函数和返回函数的概念。

词法作用域

在 JavaScript 中,词法作用域是一个概念,其中变量的作用域在代码编译时根据代码结构确定。例如,嵌套函数可以访问父函数作用域中的变量,这称为词法作用域。

嵌套函数

您可以在函数内部定义函数,内部函数称为嵌套函数。让我们通过下面的示例来学习它。

示例

在下面的示例中,我们在 outer() 函数内部定义了 inner() 函数。此外,inner() 函数在 outer() 函数内部执行。

当我们执行 outer() 函数时,它还会执行 inner() 函数,这是一个嵌套函数。

<html>
<body>
   <p id = "demo"> </p>
   <script>
      const output = document.getElementById("demo");
      function outer() {
         output.innerHTML += "The outer function is executed! <br>";
         function inner() {
            output.innerHTML += "The inner function is executed! <br>";
         }
         inner();
      }
      outer();
   </script>
</body>
</html>

输出

The outer function is executed!
The inner function is executed!

返回函数

当任何函数返回函数而不是值或变量时,它被称为返回函数。让我们看看下面的例子。

示例

在下面的代码中,outer() 函数返回函数定义,我们将其存储在 'func' 变量中。之后,我们使用 'func' 变量来调用其中存储的函数。

<html>
<head>
   <title> JavaScript - Returning function </title>
</head>
<body>
   <p id = "demo"> </p>
   <script>
      const output = document.getElementById("demo");
      function outer() {
         output.innerHTML += "The outer function is executed! <br>";
         return function inner() {
            output.innerHTML += "The inner function is executed! <br>";
         }
      }
      const func = outer();
      func();
      func();
   </script>
</body>
</html>

输出

The outer function is executed!
The inner function is executed!
The inner function is executed!

现在,您已经学习了学习闭包所需的先决条件。

JavaScript 闭包的定义有点令人困惑,但我们将逐步学习闭包概念,以便您能理解。

计数器困境

例如,您创建一个计数器来递增和递减变量。如下所示,您需要使用全局变量作为计数器。

示例

在下面的示例中,全局变量 'cnt' 初始化为 100。每当执行 decrement() 函数时,它都会将 'cnt' 变量的值减 1。

<html>
<body>
   <p id = "demo"> </p>
   <script>
      const output = document.getElementById("demo");
      var cnt = 100;
      function decrement() {
         cnt = cnt - 1;
         output.innerHTML += "The value of the cnt is: " + cnt + "<br>";
      }
      decrement();
      decrement();
      decrement();
   </script>
</body>
</html>

输出

The value of the cnt is: 99
The value of the cnt is: 98
The value of the cnt is: 97

上面的代码可以完美地作为递减计数器工作,但问题是 'cnt' 变量可以在代码中的任何位置访问,代码的任何部分都可以在不执行 decrement() 函数的情况下更改它。

在这里,JavaScript 闭包就派上用场了。

示例:JavaScript 闭包

在下面的示例中,counter() 函数返回 decrement() 函数。'cnt' 变量是在 counter() 函数内部定义的,而不是在全局作用域中。

decrement() 函数将 'cnt' 的值减 1 并打印在输出中。

'func' 变量包含 decrement() 函数表达式。每当您执行 func() 时,它都会调用 decrement() 函数。

<html>
<body>
   <p id = "demo"> </p>
   <script>
      const output = document.getElementById("demo");
      function counter() {
         let cnt = 100; // Works as a global variable for the decrement function.
         return function decrement() {
            cnt = cnt - 1;
            output.innerHTML += "The value of the cnt is: " + cnt + "<br>";
         }
      }
      const func = counter(); // Returns the decrement() function expression
      func();
      func();
      func();
   </script>
</body>
</html>

输出

The value of the cnt is: 99
The value of the cnt is: 98
The value of the cnt is: 97

现在,让我们再次记住闭包的定义。它表示嵌套函数可以访问外部函数作用域中的变量,即使外部函数的执行已经结束。

在这里,counter() 函数的执行已经结束。您仍然可以调用 decrement() 函数并访问具有更新值的 'cnt' 变量。

让我们看看另一个闭包的例子。

示例

在下面的示例中,name() 函数返回 getFullName() 函数。getFullName() 函数将字符串与在外部函数作用域中定义的 'name' 变量合并。

<html>
<head>
   <title> JavaScript - Closure </title>
</head>
<body>
   <p id = "demo"> </p>
   <script>
      const output = document.getElementById("demo");
      function name() {
         let name = "John";
         return function getFullName() {
            return name + " Doe";
         }
      }

      const fullName = name();
      output.innerHTML += "The full name is " + fullName();
   </script>
</body>
</html>

输出

The full name is John Doe

闭包的优点

以下是 JavaScript 中闭包的一些优点:

  • 封装性 − 闭包允许开发者隐藏或封装数据。它使数据私有化,无法从全局作用域访问。因此,您可以只公开所需的变量和函数,并隐藏代码的其他内部细节。

  • 保持状态 − 函数即使在外部函数执行完成后,也能记住其词法作用域。因此,开发者可以像我们在上面的例子中维护计数器状态一样,维护状态。

  • 提高内存效率 − 闭包允许您有效地管理内存,因为您只需保留对必要变量的访问权限,而无需在全局范围内定义变量。

广告