JavaScript 作用域与执行上下文详解
本文详细解析 JavaScript 中的作用域机制与执行上下文。重点阐述函数作用域的特性、变量声明规范,以及全局与函数执行上下文的创建、压栈与销毁过程,澄清作用域确定与变量值获取的区别。
关联阅读:谈谈我对JavaScript原型和闭包系列理解(随手笔记6) 谈谈我对JavaScript原型和闭包系列理解(随手笔记8)
JavaScript 作用域基础
作用域
引用《JavaScript语言精粹和编程实践》中关于作用域的定义:
变量作用域即变量的可见性。其核心功能在于信息隐蔽,旨在解决数据“割据”问题。
JavaScript传统上不具备块级作用域(ES6引入的let关键字可在{}、if、for等块内声明变量,且作用域限定于该块,同时let声明的变量不存在变量提升现象。此处暂不深入讨论,因本人亦是偶然获悉。)。
在编写代码时,应避免在“块”内部声明变量,而应在代码起始处统一声明,以消除潜在歧义。
for(var i = 0; i < 10; i++) { //不好的声明方式
//...
}
console.log(i);
/*----------------------------------------------*/
var i = 0; //好的声明方式
for(i = 0; i < 10; i++) {
//....
}
console.log(i);
函数作用域与变量声明规范
除全局作用域外,JavaScript还存在函数作用域。
声明变量时,全局代码需在最前端声明;函数内声明的变量则需置于函数体之前。此外,声明变量时必须使用"var"操作符。
var a = 10, //全局作用域
b = 20;
function fn() { //fn函数作用域
var a = 100,
c = 300;
function bar() { //bar函数作用域
var a = 1000,
d = 4000;
}
}
全局代码、fn函数及bar函数各自构成独立的作用域。作用域之间存在层级关系,该关系由函数的创建位置决定。若bar函数在fn函数作用域内创建,则“fn函数作用域”即为“bar函数作用域”的上级。
作用域的核心价值在于隔离变量,确保不同作用域下的同名变量互不冲突。
--------------------------------------------------------------------------------
执行上下文的生命周期
作用域在函数定义时即已确定,而非在函数调用时确定。

1、程序加载阶段即确定全局上下文环境,并随程序执行对变量进行赋值。

2、执行至第36行,调用fn(10),创建fn函数的执行上下文环境并压栈,同时将该上下文设为活动状态。

3、执行至第32行,调用bar(100),创建bar(100)函数的执行上下文环境并压栈,同时将该上下文设为活动状态。

4、bar(100)调用结束后出栈,其上下文环境被销毁。随后执行第33行,调用bar(200),创建bar(200)函数的执行上下文环境并压栈,同时将该上下文设为活动状态。

5、bar(200)调用结束后出栈,其上下文环境被销毁。此时控制权交还至fn(10)上下文环境,使其再次成为活动状态。

6、当fn(10)调用结束并出栈,其上下文环境被销毁。控制权最终交还至全局执行上下文环境。

至此,上述代码的执行流程结束。
此处引用原作者提供的完整图示:

总结:
作用域仅作为“地盘”,需通过对应的执行上下文环境来获取变量值。同一作用域下,不同调用会产生不同的执行上下文环境,进而导致变量值的差异。因此,作用域中变量的值是在执行过程中确定,而作用域本身在函数创建时即已固定。若需查找某作用域下特定变量的值,必须定位至该作用域对应的执行上下文环境,并从中检索变量值。
JS语法错误会阻止加载吗?区分SyntaxError与资源加载失败
2026-11-05 16:08:46
JavaScript常见SyntaxError语法错误排查与修复方法
2026-11-05 13:58:44
JS中slice、substring和substr截取字符串的区别
2026-11-05 13:43:47
JS 转 TS 手动迁移最佳实践:从配置 tsconfig 到消除 any 类型
2026-11-05 11:53:32
为什么JS数组push后得到的是数字而不是数组?
2026-11-05 09:59:18
