什么是闭包?
JavaScript 闭包(Closure)概念 允许嵌套函数访问在父函数的作用域中定义的变量,即使父函数的执行已经完成。简而言之,你可以使用闭包使全局变量成为 local 或 private。
JavaScript 闭包基本上是函数及其词法环境的组合。这允许内部函数访问外部函数范围。每次在函数创建时创建函数时,都会创建一个 Closure。
在开始学习闭包(Closure)的概念之前,你需要了解词法作用域、嵌套函数和返回函数的概念。
词法范围
在 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 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 inner function is executed!
The inner function is executed!
现在,您了解了学习闭包所需的先决条件。
JavaScript 闭包的定义有点混乱,但我们会一步一步地学习闭包的概念,这样你就会明白。
反困境( A Counter Dilemma)
例如,您创建计数器来递增和递减变量。如下所示,您需要对 counter 使用全局变量。
例在下面的示例中,全局变量 '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: 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; // 用作递减函数的全局变量。
return function decrement() {
cnt = cnt - 1;
output.innerHTML += "The value of the cnt is: " + cnt + "<br>";
}
}
const func = counter(); // 返回递减()函数表达式
func();
func();
func();
</script>
</body>
</html>
输出
The value of the cnt is: 98
The value of the cnt is: 97
现在,让我们再次记住 closure 的定义。它表示嵌套函数可以从外部函数的作用域访问变量,即使外部函数的执行已完成。
在这里,counter() 函数的执行结束了。不过,您可以调用 decrement() 函数并使用更新的值访问 'cnt' 变量。
让我们看另一个 closure 的例子。
例在下面的示例中,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>
输出
闭包(Closure)的好处
以下是 JavaScript 中闭包的一些好处 -
- 封装 − 闭包允许开发人员隐藏或封装数据。它使数据私有化,并且无法从全局范围访问。因此,您可以只公开所需的变量和函数,并隐藏代码的其他内部详细信息。
- 保留状态 − 即使外部函数的执行已完成,函数也会记住其词法范围。因此,开发人员可以像在上面示例中维护计数器的状态一样维护状态。
- 提高内存效率 − 闭包允许您有效地管理内存,因为您只能保留对必要变量的访问权限,而不需要全局定义变量。