[JS]闭包和词法环境
小开开 2021-08-27 09:16:00 2021-08-27 127 0
词法环境
词法环境(lexical environment)由两个部分组成:
- 环境记录——一个存储所有局部变量作为其属性的对象。
- 对外部词法环境的引用,与外部代码相关联。
全局词法环境在脚本执行前创建,它没有更外层的词法环境。
// 全局词法环境let a = 'hello' // => hellolet b // => undefinedb = 'world' // => world
a 和 b 作为环境记录这一对象的属性,它们被声明时就被赋予值或在之后的过程中被赋予值。
let who = '小明'function say(content) { let date = new Date() alert(`${who}说:${content} | at ${date}`)}say('你好,世界!')
say() 函数引用了外部词法环境(也就是环境记录的属性 who ),因此能够构成一个词法环境。函数访问外部词法环境时,首先会搜索内部词法环境,然后搜索外部词法环境,然后搜索更外部的词法环境,以此类推,直到全局词法环境。
闭包
一个函数和其词法环境的组合就是闭包。也就是说,闭包让你可以在一个内层函数中访问到其外层函数的作用域。
原文闭包 - JavaScript | MDN。
现在,讲一讲闭包的作用是什么。count() 函数累加局部变量 record ,执行多次之后是否成功完成累加?
function count() { let record = 0 return record++}let v = 0v = count()v = count()console.log(v) // 0
执行了两次 count() 函数,变量 v 依旧是 0 ,并未改变初始值。
对象允许我们将属性与一个或者多个方法相关联,对象被创建之后,会保存在内存中,不会立即被销毁。所以,操作对象属性可以完成累加操作。
let counter = { _record: 0, set count(record) { this._record = record }, get count() { return this._record++ }}let v = 0v = counter.countv = counter.countconsole.log(v) // 1
但是,我们的目的是利用函数就可以实现累加操作。除了操作对象属性的方式和将局部变量提升为全局变量的方式以外,那就是利用闭包。
function count() { let record = 0 return function f() { return record++ }}let v = 0v = count()()v = count()()console.log(v) // 1
f() 嵌套在 count() 函数中,因此称之为 f() 是 count() 的嵌套函数。而嵌套函数引用了 count() 函数作用域内的局部变量 record ,该过程叫做词法环境。
所以,闭包 = 词法环境 + 函数。
在闭包的作用下,我们多次调用 count()() 实现了累加的操作。