读懂JavaScript预编译流程
大家都知道JavaScript是一种解释型的语言,即编译一行,执行一行,那么预编译又是什么呢?
JavaScript运行三部曲
- 语法分析
- 预编译
- 解释执行
所谓语法分析就是JS引擎通篇检查代码中的语法错误;解释执行便是执行代码了;而预编译可以理解为在内存中开辟一些空间,用来存放一些变量与函数。
预编译在什么时候发生
预编译分为全局预编译和函数预编译:全局预编译发生在页面加载完成时执行,而函数预编译发生在函数执行的前一刻。
全局预编译
JavaScript在语法分析完毕后,会进入全局预编译的流程。全局预编译可以分为以下四步:
- 创建
GO
(Global Object,全局执行期上下文,在浏览器中为window)对象; - 寻找
var
变量声明,并赋值为undefined
; - 寻找
function
函数声明,并赋值为函数体; - 执行代码。
函数预编译
在函数执行的前一刻,会进入函数预编译的流程。函数预编译可以分为以下五步:
- 创建
AO
(Activation Object,活跃对象、函数执行期上下文) - 寻找
形参
和var
变量声明,并赋值为undefined
; - 形参实参相统一(也就是把实参的值赋给形参);
- 寻找函数声明,并赋值为函数体;
- 函数执行
需要注意的是:在预编译的过程中,只看关键字var
和function
,而if
不会影响到预编译
实例分析
js
// 全局预编辑
var test = 1;
function test () {
console.log(2);
}
console.log(test);
// 1.生成GO对象
// GO = {}
// 2.寻找var变量声明,并赋值为undefined
// GO = {
// test: undefined
// }
// 3.寻找函数声明,并赋值为函数体
// GO = {
// test: function test () { ... }
// }
// 4.预编译完成、执行
// GO = {
// test: 1; // test赋值为1
// }
// 所以结果为:1
js
// 函数预编译
function test (a) {
console.log(a);
var a = 1;
console.log(a);
function a () {}
console.log(a);
var b = function () {}
console.log(b);
function c () {}
}
test(2);
// 1.生成AO对象
// AO = {}
// 2.寻找形参和var变量声明,并赋值为undefined
// AO = {
// a: undefined(形参) -> undefined(var声明)
// b: undefined(var声明)
// }
// 3.形参实参相统一
// AO = {
// a: 2
// b: undefined
// }
// 4.寻找函数声明,并赋值为函数体
// AO = {
// a: function a () {}
// b: undefined
// }
// 5.函数执行
// 函数体内:
// 第一行:结果为 function a () {}
// 第二行赋值
// AO = {
// a: 1
// b: undefined
// }
// 第三行:结果为 1
// 第五行:结果为 1
// 第六行赋值
// AO = {
// a: 1
// b: function () {}
// }
// 第七行:结果为 function () {}
js
// 全局预编译
var x = 1,
y = z = 0;
function add (n) {
return n = n + 1;
}
y = add(x);
function add (n) {
return n = n + 3;
}
z = add(x);
console.log(x, y, z);
// 1.生成GO对象
// GO = {}
// 2.寻找var变量声明,并赋值为undefined
// GO = {
// x: undefined
// y: undefined
// z: undefined
// }
// 3.寻找函数声明,并赋值为函数体
// GO = {
// x: undefined
// y: undefined
// z: undefined
// add: function add (n) { return n = n + 1; } => function add (n) { return n = n + 3; }
// }
// 4.执行
// 赋值
// GO = {
// x: 1
// y: 0
// z: 0
// add: function add (n) { return n = n + 3; }
// }
// y = add(x) 输出 4
// z = add(x) 输出 4
// 结果 x = 1; y = 4; z = 4
js
var a = 5;
function test () {
a = 0;
console.log(a);
console.log(this.a);
var a;
console.log(a);
}
test();
// AO = {
// a: undefined
// this: window
// }
// =>
// AO = {
// a: 0;
// this: window;
// }
// 函数体内第二行输出 0
// 第三行输出 5
// 第五行输出 0
new test();
// AO = {
// a: undefined
// this: {}(实例化对象)
// }
// AO = {
// a: 0;
// this: {}(实例化对象)
// }
// 函数体内第二行输出 0
// 第三行输出 undefined
// 第五行输出 0
js
function test () {
console.log(foo);
var foo = 2;
console.log(foo);
console.log(a);
}
test();
// 1.AO = {}
// 2.AO = {
// foo: undefined
// }
// 3.执行
// undefined
// 2
// Uncaught ReferenceError: a is not defined
js
function a () {
var test;
test();
function test () {
console.log(1);
}
}
a();
// 1.AO = {}
// 2.AO = {
// test: undefined
// }
// 3.AO = {
// test: function test () { ... }
// }
// 4.执行
// // 输出 1
js
var name = '222';
var a = {
name: '111',
say: function () {
console.log(this.name);
}
}
var fun = a.say;
fun();
a.say();
var b = {
name: '333',
say: function (fun) {
fun();
}
}
b.say(a.say);
b.say = a.say;
b.say();
// 1.GO = {}
// 2.GO = {
// name: undefined
// a: undefined
// fun: undefined
// b: undefined
// }
// 3.执行
// GO = {
// name: '222'
// a: {
// name: '111',
// say: function () {
// console.log(this.name);
// }
// }
// fun: function () {
// console.log(this.name);
// }
// b: undefined
// }
// fun(); 输出 '222'
// a.say(); 输出 '111'
// GO = {
// name: '222'
// a: {
// name: '111',
// say: function () {
// console.log(this.name);
// }
// }
// fun: function () {
// console.log(this.name);
// }
// b: {
// name: '333',
// say: function (fun) {
// fun();
// }
// }
// }
// b.say(a.say); 输出 '222' 函数体内this指向window
// b.say = a.say;
// b.say(); 输出 '333'
js
a = 1;
function test (e) {
function e () {}
arguments[0] = 2;
console.log(e);
if (a) {
var b = 3;
}
var c;
a = 4;
var a;
console.log(b);
f = 5;
console.log(c);
console.log(a);
}
var a;
test(1);
console.log(a);
console.log(f);
// 1.GO = {}
// 2.GO = {
// a: undefined
// }
// 3.GO = {
// a: undefined
// test: function test (e) { ... }
// }
// 4.执行
// GO = {
// a = 1;
// }
// 5.AO = {}
// 6.AO = {
// e: undefined
// b: undefined // 无视if
// c: undefined
// a: undefined
// }
// 7.AO = {
// e: 1; // 形参实参相统一
// ...
// }
// 8.AO = {
// e: function e () {};
// ...
// }
// 9.执行
// AO = {
// e: 2; // 函数体内第二行
// ...
// }
// 第三行输出 2
// AO = {
// e: 2
// b: undefined // a为undefined if 语句进不去
// ...
// }
// AO = {
// e: 2
// b: undefined
// c: undefined
// a: 4
// }
// console.log(b) 输出 undefined
// GO = {
// f: 5
// ...
// }
// console.log(c) 输出 undefined
// console.log(a) 输出 4
// 函数执行完毕
// 全局
// console.log(a) 输出 1
// console.log(f) 输出 5