0%

预编译.md

转载

关于变量提升的原因是和预编译有关的。

预编译什么时候发生

全局预编译:页面加载完成时执行

函数预编译:函数执行的前一刻

全局预编译

  • 创建GO(Global Object,全局执行期上下文,在浏览器中为window)对象;
  • 寻找var变量声明,并赋值为undefined;
  • 寻找function函数声明,并赋值为函数体;
  • 执行代码。

例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
var x = 1,
y = z = 0;

function add (n) {
return n = n + 1;
}

y = add(x);
function add (n) {
return n = n + 3;
}

z = add(x)

接下来我们来按照前面的步骤详细分析它的预编译执行过程:

  1. 创建一个GO对象

    1
    2
    3
    GO{
    // 对象内容为空
    }
  2. 寻找var变量声明,并赋值为undefined

    1
    2
    3
    4
    5
    Go{
    x: undefined
    y: undefined
    z: undefined
    }
  3. 寻找function函数声明,并赋值为函数体

    1
    2
    3
    4
    5
    6
    GO{
    x: undefined
    y: undefined
    z: undefined
    add: function add (n) { return n = n + 1; } => function add (n) { return n = n + 3; }
    }
  4. 按顺序执行代码

1
2
3
4
5
6
7
8
9
10
11
12
13
var x = 1,
y = z = 0;

function add (n) {
return n = n + 1;
} //预编译环节已经进行了变量提升,故执行时不在看这行代码

y = add(x);
function add (n) {
return n = n + 3;
}//预编译环节已经进行了变量提升,故执行时不在看这行代码,但是这一函数覆盖了前面的add函数

z = add(x)
1
2
3
4
5
6
7
故而我们的GO对象变成了
GO{
x: 1
y: 0
z: 0
add: function add (n) { return n = n + 3; }
}

所以我们可以知道,此时x的值为1,y的值为4,z的值为4.

函数预编译

  • 创建AO对象,执行期上下文。
  • 寻找函数的形参和变量声明,将变量和形参名作为AO对象的属性名,值设定为undefined.
  • 将形参和实参相统一,即更改形参后的undefined为具体的形参值。
  • 寻找函数中的函数声明,将函数名作为AO属性名,值为函数体。

例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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);

接下来我们来按照前面的步骤详细分析它的预编译执行过程:

  1. 创建AO对象

    1
    2
    3
    AO{
    // 空对象
    }
  2. 寻找函数的形参和变量声明,将变量和形参名作为AO对象的属性名,值设定为undefined.

    1
    2
    3
    4
    AO{
    a: undefined,
    b: undefined
    }
  3. 将形参和实参相统一

1
2
3
4
AO{
a: 1,
b: undefined
}
  1. 寻找函数中的函数声明,将函数名作为AO属性名,值为函数体。
1
2
3
4
5
AO{
a: function(){}
b: undefined
d: function(){}
}

函数开始逐行顺序执行:

1
2
3
4
5
6
7
8
9
10
11
12
13
function fn(a){
console.log(a);// 输出functiona(){}
var a = 123;
console.log(a);// 输出123

function a(){};//预编译环节已经进行了变量提升,故执行时不在看这行代码
console.log(a);// 输出123

var b = function(){};//这个是函数表达式不是函数声明,故不能提升,会对AO中的b重新赋值
console.log(b);//输出function(){}

function d(){};
}

综合案例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
var a = 1;
console.log(a);
function test(a) {
console.log(a);
var a = 123;
console.log(a);
function a() {}
console.log(a);
var b = function() {}
console.log(b);
function d() {}
}
var c = function (){
console.log("I at C function");
}
console.log(c);
test(2);
  • 全局预编译
1
2
3
4
5
6
7
8
9
10
11
12
13
14
GO{
a: undefined,
c: undefined,
test: function(a) {
console.log(a);
var a = 123;
console.log(a);
function a() {}
console.log(a);
var b = function() {}
console.log(b);
function d() {}
}
}
  • 按顺序执行代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
GO{
a: 1,
c: function (){
console.log("I at C function");
},
test: function(a) {
console.log(a);
var a = 123;
console.log(a);
function a() {}
console.log(a);
var b = function() {}
console.log(b);
function d() {}
}
}
  • 执行到test(2),函数开始预编译
  • 3.1
1
2
3
4
AO{
a: undefined,
b: undefined
}
  • 3.2
1
2
3
4
5
6
7
8
9
10
11
AO{
a: 2,
b: undefined
}


AO{
a: function(){},
b: undefined,
d: function(){}
}
  • 执行结果
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
var a = 1;
console.log(a); // 1
function test(a) {
console.log(a); // function a() {}
var a = 123;
console.log(a); // 123
function a() {}
console.log(a); // 123
var b = function() {}
console.log(b); // function b() {}
function d() {}
}
var c = function (){
console.log("I at C function");
}
console.log(c); // function c(){ console.log("I at C function"); }
test(2);