读懂JavaScript预编译流程

前端开发
2020年02月13日
440

大家都知道JavaScript是一种解释型的语言,即编译一行,执行一行,那么预编译又是什么呢?

JavaScript运行三部曲

  1. 语法分析
  2. 预编译
  3. 解释执行

所谓语法分析就是JS引擎通篇检查代码中的语法错误;解释执行便是执行代码了;而预编译可以理解为在内存中开辟一些空间,用来存放一些变量与函数。

预编译在什么时候发生

预编译分为全局预编译函数预编译:全局预编译发生在页面加载完成时执行,而函数预编译发生在函数执行的前一刻。

全局预编译

JavaScript在语法分析完毕后,会进入全局预编译的流程。全局预编译可以分为以下四步:

  1. 创建GO(Global Object,全局执行期上下文,在浏览器中为window)对象;
  2. 寻找var变量声明,并赋值为undefined
  3. 寻找function函数声明,并赋值为函数体;
  4. 执行代码。

函数预编译

在函数执行的前一刻,会进入函数预编译的流程。函数预编译可以分为以下五步:

  1. 创建AO(Activation Object,活跃对象、函数执行期上下文)
  2. 寻找形参var变量声明,并赋值为undefined;
  3. 形参实参相统一(也就是把实参的值赋给形参);
  4. 寻找函数声明,并赋值为函数体;
  5. 函数执行

需要注意的是:在预编译的过程中,只看关键字varfunction,而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