JS预编译
Chao 工程师

前言

在说预编译原理之前,我们先来了解两个名词。GO(global object):全局执行上下文;AO(activation object): 活跃对象,函数执行上下文。
预编译的过程跟这两个东西密切相关,其过程可归纳为两步:
1.检查通篇的语法错误;
2.解释一行,执行一行。

AO

我们先看下面这个例子,看你认为的结果和实际结果相差多少:

1
2
3
4
5
6
7
8
9
10
11
12
function test(a){
console.log(a)
var a = 1
console.log(a)
function a() {}
console.log(a)
var b = function () {}
console.log(b)
function d(){}
}
test(2) // 打印结果: ƒ a() {},1, 1,ƒ a() {}

看到结果之后是不是很震惊,跟你想想的完全不一样。这是为什么呢?当然跟我们前面提到的AO对象有关啦。在函数内部程序是按照AO的流程进行编译和执行的,且看AO执行流程:

1.寻找形参和变量声明;
2.实参值赋值给形参;
3.找函数声明;
4.执行,赋值。

我们现在依照这个流程来走一下上面这段程序。
1、首先我们寻找函数内部的形参和声明的变量,有变量a:undefined, 变量b: undefind
2、第二步将实参值赋值给形参。a变成了2
3、然后寻找函数声明,a: function a() {}, d: function d(){}
4、最后执行函数并赋值。a = 1, b = function () {} ,此时你是不是在想a现在不是等于1了吗,应该前三个都打印1呀,但是你忽略了第四步的要点,执行并赋值,因为第一行console.log(a) 在未赋值之前,此时a的值在第三步中已经变成了函数**function a() {},所以此时打印function a() {}**。
上面的四条你如果觉得可以用以有点难理解,请结合以下结构阅读:

1
2
3
4
5
6
7
8
AO = {
a: undefined ->
2 ->
function a(){} ->
1,
b: undefined -> function (){},
d: function d() {}
}

注意: 函数执行完成以后,AO是要销毁的,AO是一个即时的存储容器

趁热打铁,在来一道题:

1
2
3
4
5
6
7
8
9
10
11
12
function test(a,b) {
console.log(a);
c = 0;
var c;
a = 5;
b = 6;
console.log(b)
function b(){}
function d(){}
console.log(b)
}
test(1) // 打印:1, 6, 6

使用AO图解

1
2
3
4
5
6
7
8
9
10
11
AO = {
a: undefined ->
1 ->
5,
b: undefined ->
function b(){} ->
6,
c: undefined ->
0,
d: function d(){}
}

到这里是不是对预编译过程是不是有了清晰的理解了。

GO

同样先上一道例子:

1
2
3
4
5
var a = 1;
function a(){
console.log(2)
}
console.log(a)

很多人可能知道这里会打印1,但是他无法说服自己为什么会打印1,而不是打印function a(){},甚至是2。
我们先看看全局上下文js的预编译过程

1.找变量声明;
2.找函数声明;
3.执行,赋值。

现在用这三步流程来解析以下上面的代码块
1、找到变量声明a,此时值为undefined;
2、然后找到函数声明a,此时值为function a(){};
3、最后执行代码块,并赋值,此时a = 1。

下面用GO结构表示

1
2
3
4
5
GO = {
a: undefined ->
function a(){} ->
1
}

很多代码块我们可能只知道结果,但是不知道为什么是这样的结果,当我们弄清楚了预编译的过程,就很容易知道js代码块执行过程到底发生了什么。

基础夯实

看了上面的解释,你可能以为你掌握了预编译过程,那么请你做做下面的几道题,看看有几道题能答对呢(先别着急看后面答案和在浏览器中运行哟)。

例一:

1
2
3
4
5
6
7
8
9
10
11
var b = 3;
console.log(a)
function a(a) {
console.log(a)
var a = 2
console.log(a)
function a() {}
var b = 5;
console.log(b)
}
a(1)
1
2
3
4
5
6
7
8
9
10
11
GO = {
b: undefined -> 3
a: function a(a) {}
}
AO-a = {
a: undefined ->
1 ->
function a() {} ->
2
b: undefined -> 5
}

例二:

1
2
3
4
5
6
7
8
9
10
a = 1;
function test() {
console.log(a);
a = 2;
console.log(a);
var a = 3;
console.log(a)
}
test()
var a;
1
2
3
4
5
6
7
GO = {
a: undefined -> 1,
test: function test() {}
}
AO = {
a: undefined -> undefined -> 2 -> 3
}

例三:

1
2
3
4
5
6
7
8
9
10
11
12
function test(){
console.log(b);
if(a){
var b = 2;
}
c = 3;
console.log(c)
}
var a;
test();
a = 1;
console.log(a)
1
2
3
4
5
6
7
8
GO = {
a: undefined -> 1
test: function test(){},
c: 3
}
AO = {
b: undefined
}

例四:

1
2
3
4
5
6
7
function test(){
return a;
a = 1;
function a(){}
var a = 2;
}
console.log(test())
1
2
3
AO = {
a: undefined -> function a(){}
}

例五:

1
2
3
4
5
6
7
function test() {
a = 1;
function a(){}
var a =2;
return a
}
console.log(test())
1
2
3
AO = {
a: undefined -> function a(){} -> 2
}

例六:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
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)
1
2
3
4
5
6
7
8
9
10
11
GO = {
a: undefined,
test: function test(e){...}
f: 5
}
AO = {
a: undefined -> 4,
b: undefined,
c: undefined,
e: 1 -> function e(){} -> 2
}
 Comments