预编译
• js运行三部曲
1.语法分析
2.预编译
3.解释执行
语法分析:js引擎在解析js代码之前,会先通篇扫描一下,找出低级的语法错误,比如写错大括号之类的。
编译执行:我们前面提到js是一种解释型语言,编译一行执行一行,当语法分析没有问题,并且已经完成预编译阶段之后,就开始解释执行代码。
这里我们着重介绍预编译。
预编译前奏
在介绍预编译之前,我们有两个重要概念需要掌握。
1.imply global 暗示全局变量。
如果任何变量未经声明就赋值使用,此变量就会为全局对象window所有,并且成为window对象的一个属性。
或者
2.一切声明的全局变量,都是window的属性。
• 这样看不论全局变量有没有声明,似乎都会成为全局对象上的属性,那么两者之间有什么区别呢?
区别在于:经过声明的全局变量不能通过delete操作来删除,但是未经声明的全局变量可以被删除。
正是这一种特性,导致es5有一种弊端,我们总会在无形中声明一些全局变量。
这段代码的原意是:在函数体中声明两个变量a、b,然后初始化a、b都是0。但是我们这么写之后,a经过了声明,但是b却没有声明,这时候b就会成为一个全局变量。
了解这两点之后,我们正式介绍一下预编译的过程。
预编译的过程我总结为以下四步:
1.创建AO对象。
2.寻找形参和变量声明,将变量和形参作为AO对象的属性名添加到对象中,值为undefined。值得注意的是,函数声明不叫变量。
3.将实参值和形参值相统一。
4.在函数体里面寻找函数声明,将函数名作为属性名,值为这个函数的函数体。
函数在执行的前一刻会产生一个上下文,这个上下文就是Activeaction Object对象,简称AO对象。
AO = {}
这个对象是空的,但是里面有一些我们看不到的却存在的隐式属性,比如this: window属性和arguments: [];属性
这个对象用来存放一些属性和方法,这些属性和方法就按照前面的四步来产生。
这里我们用这一个样例代码来简单介绍一下预编译的过程。
首先第一步,创建一个AO对象。
var AO = {};
第二步,寻找形参值和变量声明,并且将值赋为undefined。
3.将实参值和形参值相统一。这里因为属性名都已经存在了,所以直接赋值就可以了。
4.寻找函数声明,将函数体赋值给属性。
这样在编译执行之前,我们预编译阶段创建的AO对象就是这个样子了,这个时候我们再看看分别打印的值是什么。
第一个console.log a –> function () {}
第二个console.log a –> 222 因为执行了a = 222这一行代码,所以重新赋值了。
第三个console.log b –> function () {}
• var b = function () {}这种不叫做函数声明,这个函数是赋值给b变量的,b变量是声明。
这里的var b = function () {}只是声明了b变量,在第四步寻找函数声明里面并不会把b赋值成function () {},因为后面的函数并不是声明,当代码开始解释执行之后,执行到这一行之后才把b赋值成这个函数。
• 寻找变量声明的时候,不会管里面的代码到底会不会执行,执行是后面的事,这里只负责寻找所有变量。
打印第一个a的时候并不会报错而是undefined,当a没有声明的时候才会报错,因此这里a是有声明的,只是没有赋值而已,它根本不看有没有if,if的条件是不是真对寻找变量声明都没有关系。
第二步寻找形参和变量声明时候的AO对象:
以上就是我总结的预编译的知识点,希望对大家有帮助哟!~