一、预编译前奏
- imply global:暗示全局变量。即任何变量未经声明直接赋值时,此变量会直接挂载到全局变量上
<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <title>1</title> </head> <body> <script> function f1(){ a = 10; //全局变量 window.a var b = c = 20; } console.log(a); //10 console.log(b); //undefined 这是一个局部变量 console.log(c); //20 并没有声明而是直接赋值,所以是全局变量 </script> </body> </html>
- 一切在全局环境下声明的变量,全会挂在window上
<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <title>1</title> </head> <body> <script> var a = 10; //window.a function f2(){ var b = 20; //这是局部变量,没有挂载到全局变量上 } </script> </body> </html>
二、预编译
函数预编译四部曲:
- 创建AO对象(Action Object 执行期上下文)。AO { ... }
- 寻找形参和变量声明,将形参和声明的变量作为AO的属性名,值为undefined,放在AO对象中
- 将形参和实参的值对应起来,覆盖原始的undfined值
- 寻找函数声明(不包括函数表达式),并将函数名作为AO的属性名,值为函数体放在AO对象中
function fn(a){ console.log(a); // f a(){} console.log(b); // undefined var a = 123; function a() { } var b = function (){ } console.log(b); // f(){} function d(){ } console.log(a); // 123 } fn(1);
我们按照上面的步骤分析一下他的预编译过程:
- 创建AO对象:AO{ }
- 将函数体内的形参和声明的变量挂载到AO对象中,属性值为undefined
AO{ a:undefined, b:undefined }
将形参和实参的值对应起来
AO{ a:1, b:undefined }
寻找函数声明,挂载到AO中,值为函数体
AO { a:f a(){}, b:undefined,(b是函数表达式,并不是函数声明) d:f (){} }
到此为止预编译结束。
当你调用fn函数时,函数开始依次执行代码,首先执行的是前两行: 打印时会去AO对象中寻找该变量
consonle.log(a) //f a(){} console.log(b) //undefined
然后执行的是 a = 123,所以此时AO对象为
AO { a:123, b:undefined, d:f (){} }
因为预编译时已经将变量声明和函数声明编译过,所以真正编译时跳过,然后执行 var b = function (){ }
此时AO对象为
AO { a:123, b:f (){}, d:f (){} }
所以执行到console.log(b)时,这时b的值为函数体 f(){},而a的值为123.
至此执行完毕。
全局预编译与函数预编译类似,只是没有参数项:
- 创建GO对象(Gobal Object) (其实我们所说的window对象就是GO对象)
- 寻找变量声明,将变量名挂载到GO对象上,值为undefined
- 寻找函数声明。将函数名挂载到GO对象上,值为函数体
三、练习题
做一些练习题练习一下预编译的过程:(答案自己做一下,复制代码在控制台输出一下就好了)
function test(a,b){
console.log(a);// ?
c = 0;
var c;
a = 3;
b = 2;
console.log(b);// ?
function b(){}
function d(){}
console.log(b);// ?
}
test(233);
function bar (){
return foo;
foo = 100;
function foo(){
//...
}
var foo = 11;
}
console.log(bar()); //?