JavaScript预编译过程发生了什么?

499 阅读5分钟

JS预编译过程

JavaScript 在编译的过程中都做了什么?

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

主要介绍的是 预编译 都做了什么?

这个知识点 学习 我是在渡一成哥的JavaScript课程中学习的 , 如果有兴趣可以去看看

在 js 执行预编译的过程中 , 大致发生了这三个步骤

  1. 变量声明整体提升
  2. 形参和实参统一
  3. 函数声明整体提升

先看几个简单的例子

function temp(){
    console.log(a)
}
temp()
//会报错 , a is not defined
function temp(){
	console.log(a)
	var a
}
temp()
//打印: undefined
temp()
function temp(){
	console.log(a)
	var a
}
//打印: undefined

视觉 上会报错的代码, 并没有出现报错提示, 依然能够正常执行

都是因为 预编译 的原因

局部预编译过程

注意 :

var a = 10

这是一个变量声明+变量赋值的过程, 可以拆分为

var a;
a = 10

用一段代码来作为演示

function temp(a,b){
    //打印a的结果
	console.log(a)
    //声明一个函数a
	function a(){}
    //给a进行赋值
	a=10
    //打印a的结果
	console.log(a)
    //声明一个函数b
	function b(){}
    //打印b的结果
	console.log(b)
}
//调用temp()这个函数
temp(10,5)

这段代码的执行结果为:

ƒ a(){} //第3行打印结果
10		//第9行打印结果
ƒ b(){}	//第13行打印结果

为什么会有这样的结果? 我来给大家解析一下这段代码的执行

AO 对象 : Activation Object(执行期上下文,作用是理解的作用域,函数产生的执行空间库)

变量声明整体提升

当函数开始执行的时候, 在浏览器内部会生成一个 AO 对象

就开始执行我们的预编译第一步 , 变量声明整体提升 , 会把我们在函数内声明的变量都给取出来放在AO 对象

并给他们赋值 , 默认初始值为 undefined

//a和b是形参,在定义函数时,声明的参数变量仅在函数内部可见
AO{
	a:undefined,
	b:undefined
}

形参和实参统一

预编译的第二步, 形参和实参统一

如果函数中存在 实参和形参 的数据传递, 则会有这一步, 如果不存在, 则不会有这一步

temp(10,5)

传递过来的值 , 给 AO 对象 中的变量赋值

AO{
	a:10,
	b:5
}

函数声明整体提升

预编译的第三步, 函数声明整体提升

我们声明了 函数a函数b , 再次改变了 AO 对象

//在我们的temp函数中, 我们声明了a函数和b函数
AO{
    a:fn(){},
	b:fn(){}
}

逐行解释代码

当预编译的前三步执行完后 , 就会开始逐行解释执行代码

这是预编译前三步执行后的AO 对象

AO{
    a:fn(){},
	b:fn(){}
}
function temp(a,b){
    //打印结果: fn(){}
	console.log(a)
    //因为函数已经声明过了,会直接跳过这行
	function a(){}
    //赋值操作,再次改变AO对象
    //AO{	a:10, b:fn(){}	}
	a=10
    //打印结果:10
	console.log(a)
    //因为函数已经声明过了,会直接跳过这行
	function b(){}
    //打印结果为:fn(){}
	console.log(b)
}

什么时候开始局部预编译

预编译发生在 函数执行的前一刻

  1. 变量声明整体提升
  2. 形参和实参统一
  3. 函数声明整体提升
  4. 逐行解释代码

全局预编译过程

全局预编译, 你需要先学习局部预编译

全局预编译与局部预编译不同, 全局预编译发生在页面加载完成时

GO 对象: Global Object(window 就是 GO)

//GO === window, GO 和 window 是一个东西

//这三条语句在全局中是一样的
console.log(a);
console.log(window.a);
console.log(go.a);

在全局预编译中, 会成功一个GO 对象 , 并且也会发生局部预编译的步骤

  1. 变量声明整体提升
  2. 形参和实参统一
  3. 函数声明整体提升

用一段代码演示

a = 10;
var a;
temp()
function temp(){
	console.log(a)
}

执行结果为: 10

变量声明整体提升

当页面加载完成时, 在浏览器内部会生成一个 GO 对象

就开始执行我们的预编译第一步 , 变量声明整体提升 , 会把我们在函数内声明的变量都给取出来放在AO 对象

并给他们赋值 , 默认初始值为 undefined

GO{
	a:undefined
}

形参和实参统一

此处代码并没有形参和实参,所以并没有这一步

函数声明整体提升

预编译的第三步, 函数声明整体提升

我们声明了 temp函数, 再次改变了 GO 对象

GO{
	a:undefined,
	temp:fn(){}
}

逐行解释代码

当预编译的前三步执行完后 , 就会开始逐行解释执行代码

这是预编译前三步执行后的GO 对象

GO{
	a:undefined,
	temp:fn(){}
}
//对a进行赋值操作,改变了GO对象,GO{	a:10, temp:fn(){}	}
a = 10;
//已经进行过变量声明提升,跳过该行
var a;
//当到这一步的时候,执行函数,就会在此处生成一个局部的AO对象
temp()
function temp(){
	console.log(a)
}

temp( )的执行生成了一个AO对象

AO{}
//但是AO对象中什么都没有,所以会直接执行语句
console.log(a)
//AO对象中并没有发现a,就会往上找,找上一层的作用域,直到找到第一层的GO对象中
//我们知道此时的GO:{	a:10, temp:fn(){}	}

打印结果为: 10

知识点

  • 任何全局变量都是 window 上的属性

  • 没有声明就赋值的变量,归 window 所有,就是在 GO 里面预编译

    function test(){
     var a = b =123;
     console.log(window.b);
    }
    test();
    //打印结果为123
    //b未进行定义,所以b会直接在GO中预编译,b是属于全局的属性
    
    function temp(){
    	a = 10
    }
    temp()
    function abc(){
    	console.log(a)
    }
    abc()
    //a未进行定义,所以a会直接在GO中预编译,a是属于全局的属性,所以在abc中也可以访问
    //打印结果为10