一、JavaScript引擎的“两步走”策略
v8引擎读取 JavaScript 代码后,遵循先编译,再执行的策略。
- 首先对全局代码进行编译,进行词法分析、语法分析等,将源代码转换成内部形式。随后执行全局代码,完成全局变量初始化、函数声明等操作。
- 全局代码执行完毕后,开始编译函数内部代码,编译完成后再执行函数代码。
“编译全局->执行全局->编译函数->执行函数”这种顺序能优化代码执行性能,确保代码按预期逻辑正确执行。
二、全局预编译三部曲
- 创建全局执行上下文对象(GO);
- 找变量声明,将变量名作为上下文对象的属性名,值为 undefined;
- 找函数声明,函数名作为上下文对象的属性名,值为 函数体。
三、函数体预编译四部曲
- 创建函数的执行上下文对象(AO);
- 找形参和变量声明,将新参和变量名作为上下文对象的属性名,值为 undefined;
- 函数调用时,将实参的值传递给形参,完成赋值。将实参和形参统一;
- 在函数体里面找函数声明,函数名作为上下文对象的属性名,值为 函数体。
四、预编译实战解析
例 1:
function fn(a){
console.log(a); // 输出:[Function: a]
var a = 123
console.log(a); // 输出:123
function a(){} // 函数声明
var b = function(){} // 函数表达式
console.log(b); // [Function: b]
function d(){}
var d = a
console.log(d); // 输出:123
}
fn(1);
全局预编译、执行和函数预编译过程:首先进行全局预编译,创建全局执行上下文,找变量声明(无),再找函数声明fn,值为函数体。第12行代码调用函数fn并入栈。此时创建函数fn的执行上下文,找形参a和变量声明b,d(a重复),值为undefined;将实参数和形参统一,即令a赋值为1;再找函数声明a和d,值分别为Function: a和Function: d。
注意:同一作用域中,变量名和函数名若相同,则后者直接覆盖前者。
执行过程:由函数预编译结果可知,第2行代码打印a值为Function: a;第3行a赋值为123,则第4行打印a为123;第6行b值为函数体,打印为Function: b;第9行d赋值为a即123,打印结果为123。
例 2:
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)
全局预编译、执行和函数预编译过程:首先全局预编译,创建全局执行上下文,找变量声明(无),再找函数声明foo,值为函数体。第11行代码调用函数foo并入栈。此时创建函数foo的执行上下文,找形参a,b和变量声明c,值为undefined;将实参数和形参统一,即令a赋值为1;再找函数声明b,值为Function: b。
函数执行过程:由函数预编译结果可知,第2行代码打印a值为1;第3行c赋值为0;第5行a赋值为3,第6行b赋值为2,故此时打印b值为2。
例 3:
var a = 1;
function fn(){
var a = 2;
function a(){}
console.log(a); // 输出:2
}
fn();
全局预编译、执行和函数预编译过程:首先全局预编译,创建全局执行上下文,找变量声明a,值为undefined,再找函数声明fn,值为函数体。第7行代码调用函数fn并入栈。此时创建函数fn的执行上下文,找形参(无)和变量声明a,值为undefined;再找函数声明a,值为Function: a。
函数执行过程:第3行a赋值为2,故此时打印a值为2。
例 4:
global = 100
function fn(){
console.log(global); // 输出:undefined
global = 200
console.log(global); // 输出:200
var global = 300
}
fn()
var global
全局预编译、执行和函数预编译过程:首先全局预编译,创建全局执行上下文,找变量声明global,值为undefined,再找函数声明fn,值为函数体。执行全局,第1行代码global赋值为100。第8行代码调用函数fn并入栈。此时创建函数fn的执行上下文,找形参(无)和变量声明global,值为undefined。
函数执行过程:由函数预编译结果可知,第3行代码打印global值为undefined;第4行global赋值为200,故此时打印值为200。第6行global赋值为300。