预编译的效果:
1.函数声明整体提升
即程序会将函数声明提到最前面。
test();
function test() {
}
上述程序在C语言中是不被允许的,函数在使用前必须有函数声明。但是,在JavaScript中,由于预编译的存在,上述代码是能够正常运行的。预编译会将函数声明提升到函数执行之前。
即,实际的代码执行顺序是
function test() {
}
test();
2.变量 声明提升
对于变量,不同于函数,其只会将变量的声明提升。
例如:
document.write(a); //undefined
var a = 123;
若无预编译的作用,在变量声明前使用变量会报错。
由于预编译的作用,实际的代码执行顺序是
var a;
document.write(a); //undefined
a = 123;
此时,变量的赋值并没有被提升。
对于任何变量,如果有变量未经声明就赋值,此变量就为全局对象(window)所有。
例如
a = 123; //window.a = 123;
console.log(a); //console.log(window.a);
再例如
function f() {
var a = b = 1;
}
f();
console.log(window.a); //a是已声明的局部变量,不归window所有,控制台输出结果为undefined
console.log(window.b); //b是未经声明的局部变量,且已赋值,为window所有,输出1
连续赋值的顺序是从右往左的,先将1赋给b,再将b的值赋给a。这里b属于未经声明的变量。
var a = 123; //window.a = 123;
console.log(a); //console.log(window.a);
再例如
var a = 123;
var b = 234;
var c = 345;
相当于
window {
a : 123,
b : 234,
c : 345
}
访问全局变量a就相当于在全局对象window中访问a属性。
预编译发生在函数执行的前一时刻
AO是指活跃对象,简单理解为作用域,实际上为执行期上下文。
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);
以以上程序为例说明。
预编译发生在fn(1)执行之前。
预编译:
1.创建AO对象
AO{
}
2.找形参和变量声明,形参名和变量名作为AO对象的属性名,此时属性值为undefined
AO{
a : undefined, //此属性以形参a和变量a的名字命名
b : undefined //此属性以变量b的名字命名
}
3.将形参和实参相统一
AO{
a : 1,
b : undefined
}
4.找函数声明,函数名作为属性名,函数体作为该属性的值
AO{
a : function a() {},
b : undefined,
d : function d() {}
}
预编译后执行函数
function fn(a) {
console.log(a); //function a() {}
var a = 123; //这里变量声明已经在预编译中执行过,所以函数在执行中此句相当于 a = 123;
//此时AO.a = 123
console.log(a); //123
function a() {} //此句已在预编译中执行,现在不再执行
console.log(a); //123
var b = function () {} //此句执行后,AO.b = function () {}
console.log(b); //function () {}
function d() {}
}
fn(1);
GO == window GO就是window
暗示全局变量也归GO所有。
console.log(test);
function test(test) {
console.log(test);
var test = 123;
console.log(test);
function test() {}
}
test(1);
var test = 234;
1.建立GO对象
GO{
}
2.找变量声明
GO{
test : undefined
}
3.找函数声明
GO{
test : funcion test(test) {
console.log(test);
var test = 123;
console.log(test);
function test() {}
}
}
4.执行第一句(console.log(test);
)
5.函数声明在全局预编译中已执行,跳到test(1)
,在执行函数前,进行函数预编译
6.建立AO对象
AO{
}
7.找形参和变量声明
AO{
test : undefined
}
8.形参与实参统一
AO{
test : 1
}
9.找函数声明
AO{
test : function test() {}
}
10.执行函数
……