作用域和作用域链
约 895 字大约 3 分钟
2026-03-02
作用域
作用域定义了程序中变量的有效区域,影响着变量可访问性、可见性和生命周期。简而言之:作用域决定了变量和函数在何处可用以及它们的生命周期
| 作用域类型 | 核心特点 | 生命周期 |
|---|---|---|
| 全局作用域 | 全局可访问(受模块边界影响) | 通常与页面生命周期一致 |
| 函数作用域 | 仅函数内部可访问 | 函数执行相关 |
| 块级作用域(ES6+) | let/const 受 {} 限制 | 块执行期间 |
作用域链
每个变量环境中除了有收集到的提升 "对象" 外,还包含了一个额外的外部引用,来指向外部的执行上下文,我们把这个外部引用称为 outer
如下代码将会输出 极客时间,而不是 极客邦。原因就在于作用域链的访问规则
代码
function bar() {
console.log(myName)
}
function foo() {
var myName = '极客邦'
bar()
}
var myName = '极客时间'
foo()
它的查找规则是
- 先查当前执行上下文
- 当前找不到,就沿着
outer去外层查,一直查到全局 - 最后仍找不到,抛出
ReferenceError

从图中可以看出,不管是 bar 还是 foo 二者的 outer 都指向全局执行上下文,也就意味着不管二者谁用了自身以外的变量,它们都会到全局执行上下文中的变量环境中查找,这个查找的链条就叫做作用域链
词法作用域
词法作用域(静态的作用域)指的是:在代码编写阶段时,所确定的变量、函数的可访问范围
重要
它是在代码编写时确定的,而不是在执行阶段决定的
这解释了前面的例子:
bar定义在全局作用域,它的外层链路指向全局- 即使
bar()在foo里被调用,也不会改写它的词法外层 - 所以
bar读取myName时,会命中全局的myName
图示

块级作用域里的变量查找
当代码执行到块作用域时,若代码中有 let 或 const 声明的变量,那么变量就会存放到该函数的词法环境中
function bar() {
var myName = '极客世界'
let test1 = 100
if (1) {
let myName = 'Chrome浏览器'
console.log(test)
}
}
function foo() {
var myName = '极客邦'
let test = 2
{
let test = 3
bar()
}
}
var myName = '极客时间'
let myAge = 10
let test = 1
foo()
函数的 [[Scope]]
函数的 [[Scope]] 是函数内部的隐式属性,该属性 是一个容器,内部包含了函数被创建时的作用域链,这个作用域链是一系列变量对象的列表
重要
[[ scope ]] 中保存的作用域链和变量环境之间的 outer 引用是相互关联的。当函数执行时,当前的执行上下文会与这个已经定义好的作用域链( 即 [[ scope ]] )相互作用,确保函数可以访问到所有在其作用域范围内的变量
