函数作用域由定义位置静态决定,属词法作用域;执行上下文在调用时动态创建,含VO、作用域链和this;作用域链按定义嵌套结构链接词法环境,用于变量查找。

函数作用域的核心在于“定义时决定,而非调用时决定”——这是词法作用域的本质,也是理解执行上下文的关键起点。它不随函数在哪里被调用而改变,只取决于函数写在代码的哪个位置。
词法作用域:静态确定,不可运行时更改
JavaScript 的作用域是词法的,意味着变量和函数的可访问范围,在代码被解析(编译)阶段就已固定。比如一个函数内部引用了变量 x,引擎会顺着它定义时的嵌套结构向上查找,而不是看它被谁调用。
- 即使把函数赋值给另一个变量、传入其他函数、甚至从深层嵌套中移出调用,它的上层作用域仍保持不变
- let/const 和 var 都遵循词法作用域,但它们的声明提升和初始化时机不同,影响的是变量是否“可访问”,而非作用域本身
- 唯一例外是通过 new Function() 创建的函数,它忽略外层词法环境,总在全局作用域中执行
执行上下文:函数调用时才诞生的运行环境
作用域描述“能访问什么”,而执行上下文描述“此刻正在运行什么”。每次函数被调用,都会创建一个新的执行上下文,它包含三个关键部分:变量对象(VO)、作用域链(Scope Chain)、this 值。
- 全局执行上下文只有一个,页面加载时创建,生命周期与页面一致
- 函数执行上下文在调用开始时创建,执行结束(遇到 return 或自然退出)后被销毁,由垃圾回收机制清理
- 执行上下文栈(ECStack)按调用顺序压入弹出,栈底是全局上下文,栈顶是当前正在执行的函数上下文
作用域链:连接定义与执行的桥梁
作用域链不是执行上下文本身,而是每个执行上下文内部的一个只读引用列表,它由当前函数的词法环境(LexicalEnvironment)及其所有父级词法环境串联而成。
- 变量查找严格按作用域链自内向外进行:先查当前上下文的变量对象,再查外层函数的变量对象,最后到全局对象
- 闭包之所以能“记住”外层变量,正是因为其内部函数的作用域链始终保留着对外层执行上下文变量对象的引用
- 注意:作用域链只用于标识符(变量、函数名)查找;原型链用于对象属性查找,二者完全独立
常见误区辨析
容易混淆的几个点,其实都源于没分清“定义”和“执行”这两个阶段:
- 作用域 ≠ 执行上下文:作用域是规则,是蓝图;执行上下文是实例,是运行时的快照
- 块级作用域不是执行上下文:if 或 {} 中用 let/const 声明的变量确实有块作用域,但不会创建新的执行上下文——只有函数调用才会
- this 不属于作用域体系:this 的绑定是动态的,取决于调用方式;而作用域是静态的,与调用无关

















