JavaScript 闭包
在本教程中,您将了解 JavaScript 闭包是什么以及它是如何工作的。
了解 JavaScript 闭包
在 JavaScript 函数 一章中,您了解到在 JavaScript 中变量的范围可以是全局 或局部。 从 ES6 开始,您还可以使用 let
关键字创建 块范围变量。
全局变量可以在程序的任何地方访问和操作,而局部变量只能由声明它们的函数访问和操作。
但是,在某些情况下,您希望变量在整个脚本中可用,但您不希望代码的任何部分能够意外更改其值。
让我们看看如果您尝试使用全局变量来实现这一点会发生什么:
// Global variable
var counter = 0;
// 专用于操作"counter"变量的函数
function makeCounter() {
return counter += 1;
}
// 调用函数
makeCounter();
console.log(counter); // Prints: 1
makeCounter();
console.log(counter); // Prints: 2
// 试图从外部操纵"counter"变量
counter = 10;
console.log(counter); // Prints: 10
正如你在上面的例子中看到的,counter
变量的值可以在程序的任何地方改变,而不需要调用 makeCounter()
函数(line 没有-17)。
现在,让我们尝试用局部变量实现同样的效果,看看会发生什么:
function makeCounter() {
// 局部变量
var counter = 0;
// 操作"counter"变量
return counter += 1;
}
// 调用函数
console.log(makeCounter()); // Prints: 1
console.log(makeCounter()); // Prints: 1
在这种情况下,counter
变量不能从外部操作,因为它是 makeCounter()
函数的本地变量,但是在后续函数调用后它的值也不会增加,因为每次调用函数时它都会重置 counter
变量值,您可以清楚地看到 请参见上面的示例(line no-11)。 JavaScript 闭包可以解决我们的问题。
闭包基本上是一个内部函数,它可以访问父函数的范围,即使在父函数完成执行之后也是如此。 这是通过在另一个函数中创建一个函数来完成的。 让我们看一下下面的例子,看看它是如何工作的:
function makeCounter() {
var counter = 0;
// 嵌套函数
function make() {
counter += 1;
return counter;
}
return make;
}
/* 执行 makeCounter() 函数并存储
myCounter 变量中的返回值 */
var myCounter = makeCounter();
console.log(myCounter()); // Prints: 1
console.log(myCounter()); // Prints: 2
正如您在上面的示例中看到的,内部函数 make()
是从外部函数 makeCounter()
返回的。 所以 myCounter
的值就是 make()
的内部函数(line no-14),调用 myCounter
有效地调用了 make()
。 在 JavaScript 中,函数可以分配给变量,作为参数传递给其他函数,可以嵌套在其他函数中,等等。
您还会注意到,即使 makeCounter( )
函数已经完成执行(line no-14)。 这是因为 JavaScript 中的函数形成闭包。 闭包在内部存储对其外部变量的引用,并且可以访问和更新它们的值。
在上面的示例中,make()
函数是一个闭包,其代码引用外部变量 counter
。 这意味着每当调用 make()
函数时,其中的代码都能够访问和更新 counter
变量,因为它存储在闭包中。
最后,由于外部函数已完成执行,代码的其他部分无法访问或操作 counter
变量。 只有内部函数才能独占访问它。
前面的例子也可以用匿名函数表达式来写,像这样:
// Anonymous function expression
var myCounter = (function() {
var counter = 0;
// 嵌套匿名函数
return function() {
counter += 1;
return counter;
}
})();
console.log(myCounter()); // Prints: 1
console.log(myCounter()); // Prints: 2
提示: 在 JavaScript 中,所有函数都可以访问全局范围以及它们之上的范围。 由于 JavaScript 支持嵌套函数,这通常意味着嵌套函数可以访问在更高范围内声明的任何值,包括其父函数的范围。
注意:只要您的应用程序(即您的网页)存在,全局变量就会存在。 然而,局部变量的生命周期很短,它们是在调用函数时创建的,并在函数完成执行后立即销毁。
创建 Getter 和 Setter 函数
在这里,我们将创建一个变量 secret
并使用闭包保护它不被外部代码直接操作。 我们还将创建 getter 和 setter 函数来获取和设置其值。
此外,setter 函数还会快速检查指定的值是否为数字,如果不是,则不会更改变量值。
var getValue, setValue;
// 自执行功能
(function() {
var secret = 0;
// Getter function
getValue = function() {
return secret;
};
// Setter function
setValue = function(x) {
if(typeof x === "number") {
secret = x;
}
};
}());
// Calling the functions
getValue(); // 返回: 0
setValue(10);
getValue(); // 返回: 10
setValue(null);
getValue(); // 返回: 10
提示:自执行函数也称为立即调用函数表达式(IIFE),立即执行函数,或自执行匿名函数。