• 咱们在书写js代码的时候,会发现两点和c/c++语言不一样的地方。第一个是当咱们在后面定义了一个函数以后,咱们在定义函数以前使用这个函数也是能够的。第二个是咱们在后面声明的一个变量,可是在前面调用这个变量的时候并不会报错而是undefiend。c++
这两点不一样在js中被称为函数声明提高和变量声明提高,函数声明提高是一种总体提高,它会把函数声明和函数体一块儿提高到前面。变量声明提高则是一种局部提高,它仅仅将变量的声明提早了,可是并无将赋值也一块儿提早。函数
那么为何会出现这种提高的现象呢?this
这是由于js运行的时候有一个阶段叫作预编译阶段,而咱们的声明提高现象都是发生在预编译的时候哟~es5
预编译
• js运行三部曲spa
1.语法分析3d
2.预编译对象
3.解释执行blog
语法分析:js引擎在解析js代码以前,会先通篇扫描一下,找出低级的语法错误,好比写错大括号之类的。io
编译执行:咱们前面提到js是一种解释型语言,编译一行执行一行,当语法分析没有问题,而且已经完成预编译阶段以后,就开始解释执行代码。console
这里咱们着重介绍预编译。
预编译前奏
在介绍预编译以前,咱们有两个重要概念须要掌握。
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对象:
以上就是我总结的预编译的知识点,但愿对你们有帮助哟!~