目录
第一次学的时候以为和C预处理差不多,看了下才发现区别还蛮大的;
例1:
test();//打印出 a
function test(){
console.log("a");
}
例2:
console.log(a); //打印undefined
var a = 10;
console.log(a); //直接报错
这里可以发现,跟C预处理不一样,js的预编译会先把全局变量先声明,所以才会有undefined;而C的预处理包括
- 宏定义指令(#define删除);
- 条件编译指令:处理所有的条件预编译指令。
- 头文件包含指令:处理#include 预编译指令。
- 特殊符号指令(比如注释符号)
- 等等
跟js差别蛮大的,我们这里来看预编译的例子。
1.函数体内
例
如下:
function fn(a){
console.log(a);
var a = 123;
console.log(a);
function a(){}
console.log(a);
var b = function(){}
console.log(b);
function d(){}
}
fn(1);
这里存在以a为名的变量,函数,赋值等操作,如果不懂预编译,可能会直接懵了,我们需要看预编译的顺序:
首先预编译发生在函数执行前一刻,一共四步:
- 创建AO对象Activation Object(执行期上下文);
- 找形参和变量声明,将变量和形参名作为AO属性名,值为undefined;
- 将实参值和形参统一;
- 在函数体中找函数声明,值赋予函数体;
所以我们可以把这个例子过一遍
1.首先创建AO对象
AO{
}
2.找形参和变量声明,将变量和形参名作为AO属性名,值为undefined
AO{
a:undefined
b:undefined
}
3.将实参值和形参统一
AO{
a:1
b:undefined
}
4.在函数体中找函数声明,值赋予函数体
AO{
a:function(){}
b:undefined //var b = function(){}不是函数声明,是赋值
d:function(){}
}
四步完后,创建完AO对象,执行函数 fn(a),
- 执行第一句 console.log(a);AO对象的 a 为 function(){},即打印 function(){};
- 执行第二句,因为var已经在预编译处理过,所以变量声明不看,但是 a = 123没有走过,所以 a 赋值为123;AO里 a = 123;
- 执行第三句,a已经赋值为123,所以打印123;
- 执行第四句,function a(){} 在预编译已经处理过,所以不看,a 仍为123;
- 执行第五句,继续打印123;
- 执行第六句,function b 不看,已经执行过了。所以执行 b = function(){};AO里 b = function(){};
- 执行第七句,打印机 function (){};
- 执行第八句,预编译处理过,不处理跳过;
所以结果为:
ƒ a(){}
123
123
ƒ (){}
2.全局
全局预编译和函数体内大致相同,但全局没有形参实参,所以省略第三步
- 创建GO对象Global Object(执行期上下文);
- 找变量声明,将变量作为AO属性名,值为undefined;
- 在函数体中找函数声明,值赋予函数体;
注:window 属性和 imply global属性
1.任何全局变量都是 window 的属性,我们可以这么调用
console.log(window.a); //打印 a(){},跟写console.log(a)结果相同
function a(){}
所以这里的 GO实际上跟 window是一样的。
2.所有未经声明就赋值的变量都在 imply global 属性中,实际上也就是在 GO中
例如:
function test(){
var a = b = 123;
console.log(window.a); //undefined
console.log(window.b); //打印123
}
test();
//生成 AO 对象,AO对象中含有 a,而 b 因为没有声明,属于 GO 对象
3.全局和函数体内结合,优先顺序
例1:
console.log(test);
function test(test){
console.log(test);
var test = 234;
console.log(test);
function test(){};
}
test(1);
var test = 123;
预编译顺序:
1.首先找全局变量
GO{
test:undefined
}
2.找函数声明
GO{
function test(){ ... }
}
3.开始执行,console.log(test),即打印function(){...}
4.function test 在预编译已经声明了,所以跳过,走到 test(1) 这一句,开始执行,生成 AO;
AO{
//test:undefined; 这句最后被 实参1 覆盖
//test:1; 这句最后被 function(){}覆盖
test:function(){}
}
5.所以走到函数内第一句console.log(test),打印 function(){}
6.后面的几句跟上述相同,赋值,打印234,跳过函数声明;
注:如果AO上没有要打印的变量,去GO继续找;
例2:
global = 100;
function fn(){
console.log(global);
global = 200;
console.log(global);
var global = 300;
}
fn();
var global;
首先预编译,找变量声明,var global放入 GO 对象,然后继续放入 fn函数;
然后执行程序,GO中 global = 100,执行 fn();
进入函数后,进行预编译,生成 AO对象,加入 var global ,所以第一个打印是 undefined;(先 AO没有的话,再是GO)
后面那个打印即200;
例3(重要提示)
function test(){
console.log(b);
if(a){
var b = 100;
}
console.log(b);
c = 234;
console.log(c);
}
var a;
test();
a = 10;
console.log(c);
这里 GO 和 AO 的执行顺序按照之前写的一样,遇到 if 或者其他比如 for等等,都不需要管,直接拿出变量声明即可;
所以这里的执行顺序,
- GO对象,加入 var a,加入 function test();
- 运行程序,在 test() 进入函数,然后创建 AO 对象;
- AO 对象 加入 变量声明 var b;所以打印 undefined;
- 然后执行程序;由于 a 还是 undefined,所以 if 不执行,打印 undefined;
- c = 234,未声明变量赋值,所以在 GO 中加入,打印 c 的时候,AO中没有,但是 GO 中存在,打印234;
- 然后跳出函数,console.log(c),由于 c 在 GO 中,所以打印234;