在讨论闭包之前,首先看看官方介绍

闭包(closure)是一个函数以及其捆绑的周边环境状态(lexical environment,词法环境)的引用的组合。换而言之,闭包让开发者可以从内部函数访问外部函数的作用域。在 JavaScript 中,闭包会随着函数的创建而被同时创建。

从定义中可以看到,闭包和词法环境( lexical environment )有很大的关系。

那么什么是词法环境呢? 这里先卖个关子后面介绍, 我们先从另一个角度——内存模型来切入。

内存模型与闭包

在程序运行时,变量的声明、使用和销毁都有一个生命周期。一般来说,当一个变量所在的作用域执行完毕后,内存就会被回收,这个变量也不再可访问。但是,如果存在某种机制可以让变量的生命周期延长,即使超出了它所在作用域的执行,也仍然能存活下来。闭包正是这样的一种机制。

JavaScript 中,全局变量、闭包和异步操作都能延长变量的生命周期。你可能已经注意到,这些操作类似某种形式的内部函数对外部函数的访问。

词法环境(Lexical Environment)

先查看 ECMA规范 了解 词法环境 是什么

词法环境是一种规范类型,用于定义标识符s 到基于词法嵌套的特定变量和函数 ECMAScript 代码的结构。词法环境由环境记录和对外部词法环境的可能 null 引用组成。通常,词法环境与 ECMAScript 代码的一些特定语法结构,例如函数声明一个Block语句或抓住子句的TryStatement 语句并且每次评估此类代码时都会创建一个新的词法环境。

简单来说:

举个栗子:

let data = null;
function test() {
  let name = null;
  console.log(data);
}

在上面的代码中,存在两个作用域:全局作用域和函数 test 的作用域。