阅读 190

[JS]闭包和词法环境

词法环境

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

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

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

// 全局词法环境
let a = 'hello' // => hello
let b // => undefined
b = '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 = 0
v = 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 = 0
v = counter.count
v = counter.count
console.log(v) // 1
复制代码

但是,我们的目的是利用函数就可以实现累加操作。除了操作对象属性的方式和将局部变量提升为全局变量的方式以外,那就是利用闭包。

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

let v = 0
v = count()()
v = count()()
console.log(v) // 1
复制代码

f() 嵌套在 count() 函数中,因此称之为 f() 是 count() 的嵌套函数。而嵌套函数引用了 count() 函数作用域内的局部变量 record ,该过程叫做词法环境。

所以,闭包 = 词法环境 + 函数。

在闭包的作用下,我们多次调用 count()() 实现了累加的操作。

文章分类
前端
文章标签