JavaScript 预编译机制深度解析
什么是预编译?
在 JavaScript 代码执行前,JavaScript 引擎会进行预编译阶段,处理变量声明、函数声明以及形参和实参的绑定。这个阶段发生在代码执行之前,并针对每个作用域(全局作用域和函数作用域)分别进行。
预编译步骤详解
全局作用域预编译步骤
- 创建全局执行上下文对象 (Global Object, GO)
- 查找变量声明 (使用
var
关键字) - 查找函数声明 (使用
function
关键字)
函数作用域预编译步骤
- 创建函数执行上下文对象 (Activation Object, AO)
- 查找形参和变量声明
- 将实参赋值给形参
- 查找函数声明
关键概念解析
变量提升(Hoisting)
var
声明的变量会被提升到作用域顶部:
ini
console.log(a); // 输出:undefined
var a = 1;
函数声明优先
当变量声明和函数声明同名时,函数声明优先:
javascript
function test() {
console.log(a); // 输出:function a() {}
var a = 1;
function a() {}
}
test();
作用域链
JavaScript 使用作用域链查找变量:
- 先在当前执行上下文中查找
- 找不到则向上一层作用域查找
- 直到全局作用域
javascript
global = 100;
function fn() {
console.log(global); // undefined(当前AO中有global)
global = 200;
console.log(global); // 200
var global = 300;
}
fn();
var global;
代码实例深度分析
示例1:全局预编译
ini
console.log(a); // undefined
var a = 1;
预编译过程:
- 创建 GO:
{}
- 变量声明:
{a: undefined}
- 执行:
console.log(a)
→ 输出undefined
a = 1
→ 修改为{a: 1}
示例2:函数预编译
javascript
function fn(a) {
console.log(a); // function a() {}
var a = 123;
console.log(a); // 123
function a() {}
var b = function() {};
console.log(b); // function() {}
var d = a;
console.log(d); // 123
}
fn(1);
预编译过程(函数fn的AO):
- 创建 AO:
{}
- 形参和变量声明:
{a: undefined, b: undefined, d: undefined}
- 实参赋值:
{a: 1, b: undefined, d: undefined}
- 函数声明:
{a: function, b: undefined, d: undefined}
示例3:作用域链实践
ini
function foo(a, b) {
console.log(a); // 1
c = 0;
var c;
a = 3;
b = 2;
console.log(b); // 2
function b() {}
console.log(b); // 2
}
foo(1);
AO变化过程:
css
初始:{a: undefined, b: undefined, c: undefined}
实参:{a: 1, b: undefined, c: undefined}
函数:{a: 1, b: function, c: undefined}
执行:
c=0 → {a:1, b:function, c:0}
a=3 → {a:3, b:function, c:0}
b=2 → {a:3, b:2, c:0}
最佳实践与总结
最佳实践
- 使用
let
/const
代替var
,避免变量提升问题 - 函数表达式不会提升,只有函数声明会提升
- 避免在块内声明函数(非严格模式下行为不一致)
- 保持声明在作用域顶部,提高代码可读性
ini
// 推荐写法
const myFunction = function() {
// 先声明所有变量
const value1 = 10;
let value2 = 20;
// 再执行业务逻辑
console.log(value1 + value2);
};
核心总结
- 编译顺序:全局编译 → 执行全局 → 函数编译 → 执行函数
- 声明优先级:函数声明 > 参数 > 变量声明
- 作用域隔离:每个函数调用创建独立执行上下文
- 动态绑定:函数执行时才会确定变量值