JS预编译(函数预编译和全局预编译)
一、函数预编译
预编译发生在函数执行前一步
函数预编译四部曲
- 创建AO对象(执行期上下文)
- 找形参和变量声明,将变量和形参名作为AO 属性名,值为undefined
- 将实参值和形参统一
- 在函数体里面找函数声明,值赋予函数体
例一:
<script>
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);
</script>
结果:
预编译过程:(函数马上要执行,但是还没执行)
首先创建AO对象,也就是函数它产生的存储空间库
AO{
}
然后去函数中找形参和变量声明,然后将形参和变量声明值作为AO对象的属性名,值为undefined
AO{
a:undefined,
b:undefined
}
再将形参和实参的值统一
AO{
a:1,
b:undefined
}
最后在函数体中找函数声明
AO{
a:1,
b:undefined,
}
//因为a 已经有了所以被替换了,但是值要被替换 要被替换成函数体 所以变成以下对象
AO{
a:function a() {},
b:undefined,
d:function d() {}
}
然后函数开始执行
1 function fn(a) {
2 console.log(a);
3 var a = 123;
4 console.log(a);
5 function a() {}
6 console.log(a);
7 var b = function(){}
8 console.log(b);
9 function d() {}
10 }
11 fn(1);
那么首先要执行第2行代码也就是访问AO中的a 所以打印出-:
function a() {}
然后执行第3行 也就是 var a = 123;但是应该执行a=123;因为在预编译的第一步变量a被提前了所以此时打印结果为:
123
然后再执行第5行 function a() {} 因为已经提前执行了,所以此处省略,所以第6行打印的结果为:
123
当执行到第7行时原来的undefined变为 function
AO{
a:function a() {},
b:function(){},
d:function d() {}
}
所以打印结果为:
function(){}
例二:
1 function test(a,b) {
2 console.log(a);
3 c = 0;
4 var c;
5 a = 3;
6 b = 2;
7 console.log(b);
8 function b() {}
9 function d() {}
10 console.log(b);
11 }
12 test(1);
AO创建的过程:
AO{
a:undefined,
b:undefined,
c:undefined,
}
实参形参统一:
AO{
a:1,
b:2,
c:undefined
}
找函数体:
AO{
a:1,
b:function b(){},
c:undefined,
d:function d(){}
}
然后函数执行:
AO{
a:1,
b:2,
c:0,
d:function d(){}
}
所以结果为:
例三:
function test(a,b) {
console.log(a);
console.log(b);
var b = 234;
console.log(b);
a = 123;
console.log(a);
function a() {}//函数声明
var a ;
b = 234;
var b = function() {} // 函数表达式
console.log(a);
console.log(b);
}
test(1);
执行结果
[Function: a]
undefined
234
123
123
[Function: b]
二、全局体系预编译
如图:如果直接在全局调用a变量会出现undefined
但是如果代码改为:
则结果如上图;
从而引出全局编译过
- 生成一个GO对象,Global Object,即window对象
- 找形参和变量声明,将变量和形参名作为GO属性名,值为undefined
- 在函数体里面找函数声明,值赋予函数体
上图的编译模型如下:
GO{
b:123
}
AO{
a:undefined
}
例一
console.log(test);
function test() {
console.log(test);
var test = 234;
console.log(test);
function test() {
}
}
test(1);
var test = 123;
GO{
test:function test(){}
}
AO{
test:undefined,
}
AO{
test:function (){}
}
AO{
test:234,
}
结果:
[Function: test]
[Function: test]
234
//此时存在一个问题:即GO和AO中都有test ,那么这里到底是打印那个test
答:打印AO的,如果AO没有则取GO的。
例二
var global =100;
function fn(){
console.log(global);
}
fn();
GO{
global:undefined,
fn:function(){}
}
GO{
global:undefined,
fn:function(){}
}
AO{
global:100,因为AO 本身没有global则去在GO中找即
}
例子三
global =100;
function fn(){
console.log(global);
global =200;
console.log(global);
var global =300;
console.log(global);
}
fn();
var global;
//先找声明语句var global; 那么此时global:undefined;
GO{
global:undefined
}
//然后执行global=100
GO{
global:100
}
//然后函数开始执行执行:虽然没有形参,但它有自己的global
AO{
global : undefined
}
AO{
global : 200
}
AO{
global : 300
}
结果:
undefined
200
300
例子四
function test() {
console.log(b);
if(a){
var b = 100;
}
c=234;
}
var a;
test();
a=10;
console.log(c);
//先找声明语句var a; 那么此时a:undefined;
GO{
a:undefined
}
//然后执行test();由于没有形参和内部函数声明则直接找变量声明
AO{
//由于a此时为undefined 所以 var b= 100;不会执行当函数执行到c =234时,由于函数内部没有声明语句所以它是全局变量,即window.c
b:undefined
}
GO{
a:undefined,
c:234
}
然后函数执行完 a=10
GO{
a:10,
c:234
}
结果:
undefined
234
例五
function bar() {
return foo;
foo =10;
function foo() {
}
var foo =11;
}
console.log(bar());
打印结果:
function foo() { }
例六
console.log(bar());
function bar() {
foo =10;
function foo() {
}
var foo = 11;
return foo;
}
打印结果:11
例七
a=100;
function demo(e) {
function e() {}
arguments[0] =2;
console.log(e);
if(a){
var b = 123;
function c() {}
}
var c;
a=10;
var a;
console.log(b);
f=123;
console.log(c);
console.log(a);
}
var a;
demo(1);
console.log(a);
console.log(f);
GO{
a:undefined,
demo:function(){}
}
GO{
a:100,
demo:function(){}
}
//demo执行
AO{
e:undefined,
b:undefined,
c:undefined,
a:undefined
}
//形参实参统一
AO{
e:1,
b:undefined,
c:undefined,
a:undefined
}
//在函数体里面找函数声明,值赋予函数体
AO{
e:function e(){},
b:undefined,
c:undefined,
a:undefined
}
//形式实参相映射
AO{
e:2,
b:undefined,
c:undefined,
a:undefined
}
//由于a = undefined 所以var b = 123; function c() {} 不执行
AO{
e:2,
b:undefined,
c:undefined,
a:10
}
//当执行到 f=123; 时将f 付给windo对象即
GO{
a:100,
demo:function(){},
f:123
}
结果:
2
undefined
undefined
10
100
123
预编译会造成的两种提升:
1. 函数声明整体提升
2. 函数声明提升