在讨论闭包之前,首先看看官方介绍
闭包(closure)是一个函数以及其捆绑的周边环境状态(lexical environment,词法环境)的引用的组合。换而言之,闭包让开发者可以从内部函数访问外部函数的作用域。在 JavaScript 中,闭包会随着函数的创建而被同时创建。
从定义中可以看到,闭包和词法环境( lexical environment )有很大的关系。
那么什么是词法环境呢? 这里先卖个关子后面介绍, 我们先从另一个角度——内存模型来切入。
在程序运行时,变量的声明、使用和销毁都有一个生命周期。一般来说,当一个变量所在的作用域执行完毕后,内存就会被回收,这个变量也不再可访问。但是,如果存在某种机制可以让变量的生命周期延长,即使超出了它所在作用域的执行,也仍然能存活下来。闭包正是这样的一种机制。
在 JavaScript 中,全局变量、闭包和异步操作都能延长变量的生命周期。你可能已经注意到,这些操作类似某种形式的内部函数对外部函数的访问。
先查看 ECMA规范 了解 词法环境 是什么
词法环境是一种规范类型,用于定义标识符s 到基于词法嵌套的特定变量和函数 ECMAScript 代码的结构。词法环境由环境记录和对外部词法环境的可能 null 引用组成。通常,词法环境与 ECMAScript 代码的一些特定语法结构,例如函数声明一个Block语句或抓住子句的TryStatement 语句并且每次评估此类代码时都会创建一个新的词法环境。
简单来说:
词法环境 = 环境记录 + 外部环境引用环境记录 可以暂时理解为当前作用域环境下的变量和方法(详细内容将在执行上下文中展开)外部环境引用 指向上级词法环境(如果存在)(详细将在作用域链文中展开)JavaScript 引擎遇到一个新的代码块、函数或 catch 子句时,就会创建一个新的词法环境。举个栗子:
let data = null;
function test() {
let name = null;
console.log(data);
}
在上面的代码中,存在两个作用域:全局作用域和函数 test 的作用域。
全局作用域:由于没有上层作用域,所以它的外部词法环境为 null。