作用域与执行上下文

区分词法作用域与执行上下文,解释作用域链如何查找变量,并为闭包与提升打地基。

#type / concept #status / growing #tech / dev #resource / javascript #resource / ecmascript

[!info] 关联笔记

作用域与执行上下文

这个概念为什么出现

“变量从哪来/为什么读到旧值/为什么 TDZ 报错”都混在口语“作用域”里。拆开后:

  • 作用域:写代码结构决定的可见性规则(词法)
  • 执行上下文:运行到某段代码时创建的运行环境

[!abstract] 一句话理解 作用域是编译/解析期定下的查词地图;执行上下文是运行时沿着地图真正找值的现场。

最小可运行示例

场景:折扣函数里的仓库税率

const warehouseTax = 0.03 // 全局词法环境

function priceWithTax(base) {
  const serviceFee = 2 // 函数作用域
  function finalize(total) {
    // 教学点:finalize 词法上能看见 serviceFee 与 warehouseTax
    return total + serviceFee + total * warehouseTax
  }
  return finalize(base)
}

console.log(priceWithTax(100))

期望:100 + 2 + 3 = 105

再看块级:

function demo() {
  if (true) {
    let flag = 'inside'
  }
  // console.log(flag) // ReferenceError:块级作用域
}

核心模型

作用域类型

  • 全局
  • 函数
  • 块(let/const/class 等)
  • 模块作用域

作用域链

内部函数查找标识符:自己环境 → 外层词法环境 → … → 全局。这是闭包的地基。

执行上下文包含(教学)

  • 词法环境 / 变量环境
  • 外部引用(链)
  • this 绑定(函数上下文)

每次函数调用创建新的执行上下文,压入调用栈。

与提升关系

var 函数作用域 + 提升;let/const 暂时性死区。详见 变量提升

常见误区

[!warning] 常见误区:作用域 = this this 是绑定规则;作用域是标识符解析。

本节总结

先分清“地图(作用域)”与“在地图上跑的一次调用(执行上下文)”,闭包与提升才不会玄学。

自测题

  1. 为什么内层函数能读外层 const
  2. 每次调用函数是否共享同一执行上下文?
参考答案
  1. 词法作用域链在定义时确定外层引用。
  2. 不共享;每次调用创建新上下文(闭包保留的是环境引用,不是单次栈帧本身的简单复用叙事)。
创建于 2025/1/1 更新于 2026/7/15