目录
1. 执行上下文类型
JavaScript 代码执行时,会创建三种类型的执行上下文:
1.1 全局执行上下文
- 特点:最顶层的执行环境,唯一且全局唯一。
- 创建时机:脚本首次加载时自动创建。
- 全局对象:浏览器中为
window,Node.js 中为global。 - 生命周期:伴随程序运行始终,直到页面关闭或进程退出。
var globalVar = "全局变量";
console.log(window.globalVar); // "全局变量"(浏览器环境)
1.2 函数执行上下文
- 特点:每次函数调用时动态创建。
- 创建时机:函数被调用时(即使递归调用也会新建)。
- 生命周期:函数执行完毕后销毁(闭包除外)。
function func() {
var localVar = "局部变量";
console.log(localVar); // "局部变量"
}
func();
// console.log(localVar); // 报错:localVar未定义
1.3 Eval 执行上下文
- 特点:
eval函数内部代码的执行环境(严格模式下独立作用域)。 - 创建时机:
eval函数执行时。 - 使用场景:极少使用,存在安全性和性能问题。
eval('var evalVar = "eval变量";');
console.log(evalVar); // "eval变量"(非严格模式)
2. 变量环境 vs 词法环境
2.1 变量环境(Variable Environment)
- 存储内容:
var声明的变量和函数声明。 - 变量提升:声明在编译阶段被提升并初始化为
undefined。 - 作用域特性:函数作用域。
console.log(a); // undefined(变量提升)
var a = 10;
2.2 词法环境(Lexical Environment)
- 存储内容:
let/const声明的变量、块级作用域、this绑定。 - 暂时性死区(TDZ) :声明前访问会报错。
- 作用域特性:块级作用域(ES6+)。
{
console.log(b); // 报错:Cannot access 'b' before initialization
let b = 20;
}
2.3 对比与差异
| 特性 | 变量环境(var) | 词法环境(let/const) |
|---|---|---|
| 作用域 | 函数作用域 | 块级作用域 |
| 提升行为 | 声明提升并初始化为 undefined | 提升但不初始化(TDZ) |
| 重复声明 | 允许 | 禁止 |
| 全局对象绑定 | 是(var 在全局作用域) | 否(let/const 不在全局对象上) |
3. 外部环境引用与作用域链
3.1 外部环境引用(Outer Reference)
- 定义:指向父级词法环境的指针(由函数声明时的位置决定)。
- 静态作用域:JavaScript 使用词法作用域(函数定义时决定作用域链)。
function outer() {
let outerVar = "outer";
function inner() {
console.log(outerVar); // 通过外部环境引用访问 outerVar
}
inner();
}
outer();
3.2 作用域链形成机制
- 创建阶段:执行上下文初始化时,根据函数定义位置建立外部环境引用。
- 链式结构:每个词法环境通过
[[OuterEnv]]链接到父环境,形成作用域链。 - 变量查找:沿作用域链逐级向上查找变量,直到全局环境。
3.3 作用域链查找示例
let globalVal = "global";
function parent() {
let parentVal = "parent";
function child() {
let childVal = "child";
console.log(childVal); // "child"(当前词法环境)
console.log(parentVal); // "parent"(父级词法环境)
console.log(globalVal); // "global"(全局词法环境)
}
child();
}
parent();
作用域链结构:
child 词法环境 --> parent 词法环境 --> 全局词法环境 --> null
关键点:
- 闭包通过保留外部环境引用实现跨作用域访问。
- 作用域链在函数定义时静态确定,与调用位置无关。
通过理解执行上下文、环境差异和作用域链机制,可以更清晰地掌握 JavaScript 的变量查找、闭包行为及作用域规则。