理解 JavaScript 中的闭包

509 阅读3分钟

理解 JavaScript 中的闭包

闭包是 JavaScript 的难点之一。大多数教程只告诉你,闭包就是一个函数中的另一个函数,但这只是闭包的表象。本篇文章就带你透过表象,看看闭包的本质。

写 JavaScript 而不知道什么是闭包,就如同写 Java 而不知道什么是类 --- JSON之父 Douglas Crockford

首先看看下面这一段代码:

// 这段代码理论上来讲,是一个闭包
// 但是并不纯正
var x = 0;
function addNumber() {
    return 1 + x;
}

上面这段代码并不是纯正的闭包。x 定义在全局作用域中,所以并不能保证它不被修改。说到这里要提一下,JavaScript 是一个词法作用域的语言。这意味着在函数外定义的变量可以在函数内使用,而反过来则不行。你不能在函数外部使用一个在函数内定义的变量。

下面的代码是一个闭包,x 定义在函数外部。而且在函数执行完之后 addNumber 仍然可以访问到 x 的值。

function closedFunction(x) {
    // 当我们把代码用函数包裹起来时
    // 就创建了一个函数作用域
    // 传递给函数的变量都是独立的
    function addNumber() {
        return 3 + x;
    }
    return addNumber;
}
console.dir(closedFunction(3));

在浏览器中打印出来的值证明了 x 是一个闭包

那么问题来了,如果消耗函数外部的变量就是闭包,那为什么下面这段代码不是闭包呢?

function closedFunction(x) {
    var numberItem = x + 3;
    return numberItem;
}

console.dir(closedFunction)

当我们调用函数时,会创建一个函数作用域并为它分配内存。这个过程会一直持续到函数执行结束,内存被释放为止。在函数执行结束后,作用域中的值也将永远消失。

变量的作用域不在函数外部,所以不构成闭包

但是如果用一个函数包裹另一个函数,它将创建另一个作用域 --- 它旨在告诉 JavaScript,在这个函数执行完之后不要立即销毁它。

下面这段代码中的变量 counter 是一个闭包,因为它在被调用的函数之外( Increment) 。

变量counter 是上述代码片段中的闭包

在某种程度上,闭包只是具有保留数据的函数。创建闭包其实是在告诉JavaScript记住函数中事物的状态 --- 只有被使用的变量才会被视为 闭包

因为闭包是有状态函数,它们在被调用之后会记住其私有变量数据。由于变量是私有的,外部函数无法通过显示调用来访问这些私有变量。这样可以使整个函数自成一体,并且可以保护其变量免受不必要的更改。

词法作用域

因此,JavaScript 中的闭包是一种在无需显示创建类的情况下,就能将代码模块化且自包含的方式方法。使用闭包可以实现代码的可复制性,而且不用担心污染全局作用域。

闭包不仅仅是将一个函数放在另一个函数中的行为,它是一种用于创建可防止外部更改私有变量的技术,这些变量与程序的其他部分隔离,并且具有持久性。

微信关注公众号【React从入门到精通】