JAVASCRIPT 基础教程
JAVASCRIPT & DOM
JAVASCRIPT & BOM
JAVASCRIPT 高级教程
JAVASCRIPT 示例
JAVASCRIPT 参考

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)立即执行函数,或自执行匿名函数

Advertisements