[JS]闭包和词法环境

词法环境

词法环境(lexical environment)由两个部分组成:

  1. 环境记录——一个存储所有局部变量作为其属性的对象。
  2. 对外部词法环境的引用,与外部代码相关联。

全局词法环境在脚本执行前创建,它没有更外层的词法环境。

// 全局词法环境
let a = ‘hello‘ // => hello
let b // => undefined
b = ‘world‘ // => world

ab作为环境记录这一对象的属性,它们被声明时就被赋予值或在之后的过程中被赋予值。

[JS]闭包和词法环境

let who = ‘小明‘
function say(content) {
    let date = new Date()
    alert(`${who}说:${content} | at ${date}`)
}
say(‘你好,世界!‘)

say()函数能够构成一个词法环境,因为函数引用了外部词法环境(也就是环境记录的属性who)。say()函数访问外部词法环境时,首先会搜索内部词法环境,然后搜索外部词法环境,然后搜索更外部的词法环境,以此类推,直到全局词法环境。

[JS]闭包和词法环境

闭包

一个函数和其词法环境的组合就是闭包。也就是说,闭包让你可以在一个内层函数中访问到其外层函数的作用域。ps:该定义被简化,原话在闭包 - JavaScript | MDN

首先我们看一个简单计数器案例:

function count() {
    let record = 0
    return record++
}

let v = 0
v = count()
v = count()
console.log(v) // 0

我们发现执行两次count()函数,其返回的结果依旧是0。

在面向对象编程中,对象允许我们将某些数据(对象的属性)与一个或者多个方法相关联,它维持了一个环境,可以持续操作:

let counter = {
    _record: 0,
    set count(record) {
        this._record = record
    },
    get count() {
        return this._record++
    }
}
let v = 0
v = counter.count
v = counter.count
console.log(v) // 1

如果利用闭包进行计数器操作,是否能够实现计数器案例?

function count() {
  let record = 0
  return function f() {
    return record++
  }
}

let v = count()
console.log(v()) // 0
console.log(v()) // 1

利用闭包成功实现了计数器案例。

v是执行count()时创建的f()函数的引用。v维持了一个词法环境。因此,当count()被多次调用时,变量record仍然可用,可以持续操作。

[JS]闭包和词法环境

f()函数和其词法环境是一个闭包,或者将视野往上看,count()函数的作用域下就是一个闭包,内层函数f()在闭包的作用下,可以访问外层函数的作用域内的任何局部变量。

最后,在《JavaScript权威指南》中指出“所有JavaScript函数都是闭包”。

[JS]闭包和词法环境

上一篇:css文本居中效果


下一篇:python 提供INI配置文件的操作 ConfigParser