Javascript 简明教程
JavaScript - Closures
What is Closure?
JavaScript 中的 closures 的概念允许嵌套函数访问在父函数的作用域中定义的变量,即使父函数的执行已完成。简而言之,你可以使用闭包使全局变量成为局部变量或私有变量。
JavaScript closure 基本上是函数及其 lexical environment 的组合。这允许内部函数访问外部函数的作用域。每次在函数创建时创建一个闭包。
在开始学习闭包的概念之前,你需要了解词法作用域、嵌套函数和返回函数的概念。
Nested Function
你可以在函数内部定义函数,并且内部函数称为嵌套函数。让我们通过下面的示例来了解它。
Example
在下面的示例中,我们在 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!
Returning Function
当任何函数返回函数而不是值或变量时,它称为返回函数。我们来看看下面的例子。
Example
在下面的代码中,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 闭包的定义有点令人困惑,但我们将逐步学习闭包的概念,以便你能够清楚地理解它。
A Counter Dilemma
例如,你创建计数器来对变量进行递增和递减。如下所示,你需要使用全局变量作为计数器。
Example
在下面的示例中,“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 闭包就出现了。
Example: JavaScript Closures
在下面的示例中,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”变量。
让我们来看看闭包的另一个示例。
Example
在下面的示例中,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