写点什么

【JS】预编译

用户头像
学习委员
关注
发布于: 2021 年 01 月 29 日
【JS】预编译

预编译是 js 的一个特色,也算是闭包之前最难的一个知识点。

预编译有 4 句公式,而这 4 句公式只要背熟就能理解预编译。

这 4 句公式放在本文最末尾。


本节篇幅比较长,要理解的东西也比较多。


在学习预编译之前要记住下面这句话:

JS 的声明和执行(赋值)是分开两步操作的,声明属于预编译环节。



要了解预编译,就要先了解变量声明。


变量声明又分为全局和局部。


局部变量和作用域有关,稍后会讲到。


全局变量声明要记住 2 句话

  1. 任何变量,如果未经声明就赋值,此变量就为全局变量所有。

  2. 一切声明的全局变量,都是 window 的属性。



console 可以看到输出的 window 里面,有变量 a



如果输出 window.a ,那就直接输出了 123 了。


由此可以解析上面所说的全局变量的两句话。




在全局声明的变量,称为全局变量。全局变量的作用域就是全局。


局部变量的作用域通常是在函数内部。


全局作用域称为 GO,全称:Global Object。

临时作用域成为 AO,全称:Activation Object。


而 GO 又可以理解为 window 这个对象。


GO 和 AO 要和 JS 的预编译一起理解。


预编译公式

  1. 创建 GO/AO 对象。

  2. 找形参和变量声明,将变量和形参名作为 AO 的属性名,值为 undefined。

  3. 将实参值和形参统一。

  4. 在函数体里面找函数声明,值赋予函数体。


看下面的代码,理解 GO 和 AO。


变量 a 是定义在全局上的变量。


b 是声明在全局上的函数。


所以 GO 现在就有两个值,如下图所示。


在执行这段 JS 代码前,JS 引擎会先把 JS 代码编译一遍。

这个编译的过程就是定义作用域的过程。

而最外层的作用域成为 GO,GO 里面现在就有 a 和 b,而且值都是 undefined。

上图就是完成了【预编译公式】的前两句。


由于这里是 GO,所以第三句公式是不用管的。


接下来就执行第 4 句公式。GO 就变成下图所示。


这时 b 的值就是一个函数体。


完成以上 4 句【预编译公式】后,GO 就算是定义好了。


当预编译完成后,代码会按顺序执行。


这时执行第 1 行代码。

所以 a 的值就变成 123。


第 3-5 行代码在 JS 运行时可以暂时忽略了。因为在预编译的时候已经提升了。


所以接着就运行第 7 行代码。


第 7 行代码运行后,就会执行第 3-5 行代码。由于第 3-5 行代码是一个函数体。所以这时又需要预编译一次。


函数会创建一个 AO。而且还会继承父函数的作用域下的所有东西。


下图解析


函数 b 会产生一个 AO,在这个过程中,函数 b 的 AO 首先会检查有没有形参,这里的形成是 aa。还检测到定义了一个变量 c。运行完【预编译公式】前两条,就形成上图所示的内容。


接着会执行【预编译公式】第三条:将实参值和形参统一。

所以 aa 的值这时就变成了 123(在执行函数 b 的时候,从外面传了一个值给形参 aa)。


最后在函数 b 里面寻找函数声明。由于这里没有函数声明。所以最终会形成下图所示内容。


以上就完成了函数 b 的预编译。


接下来的所有步骤都只是执行。比如执行变量 c 的赋值,最终让变量 c 的值变成 888。


以上就是 JS 的整体执行过程和预编译的原理。熟记【预编译公式】。



从这里开始就是预编译的小案例(举更多例子解析什么是预编译)


【例子 1】


上面的代码,在执行的时候(包含预编译时)会变成下图所示。




预编译公式

  1. 创建 GO/AO 对象。

  2. 找形参和变量声明,将变量和形参名作为 AO 的属性名,值为 undefined。

  3. 将实参值和形参统一。

  4. 在函数体里面找函数声明,值赋予函数体。


用户头像

学习委员

关注

反派 2019.03.19 加入

哈哈哈哈哈哈哈哈哈哈哈哈哈哈

评论

发布
暂无评论
【JS】预编译