一、前言
写这个的目的是笔者对于js变量提升一直心存疑惑,原谅我这么菜hhhhh
二、预编译在什么时候发生
预编译分为全局预编译和函数预编译:全局预编译发生在页面加载完成时执行,而函数预编译发生在函数执行的前一刻。
三、全局预编译的步骤
- 创建GO(Global Object,全局执行期上下文,在浏览器中为window)对象;
- 寻找var变量声明,并赋值为undefined;
- 寻找function函数声明,并赋值为函数体;
- 执行代码。
一个小案例
我们先来看一下下面这段代码:
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对象
GO{
// 对象内容为空
}
寻找var变量声明,并赋值为undefined
Go{
x: undefined
y: undefined
z: undefined
}
寻找function函数声明,并赋值为函数体
GO{
x: undefined
y: undefined
z: undefined
add: function add (n) { return n = n + 1; } => function add (n) { return n = n + 3; }
}
按顺序执行代码
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)
故而我们的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属性名,值为函数体。
一个小案例
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);
接下来我们来按照前面的步骤详细分析它的预编译执行过程:
创建AO对象
AO{
// 空对象
}
寻找函数的形参和变量声明,将变量和形参名作为AO对象的属性名,值设定为undefined.
AO{
a: undefined,
b: undefined
}
将形参和实参相统一
AO{
a: 1,
b: undefined
}
寻找函数中的函数声明,将函数名作为AO属性名,值为函数体。
AO{
a: function(){}
b: undefined
d: function(){}
}
函数开始逐行顺序执行:
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(){};
}
结果在代码块的注释里
一个综合案例
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);
全局预编译
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() {}
}
}
按顺序执行代码
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
AO{
a: undefined,
b: undefined
}
3.2
AO{
a: 2,
b: undefined
}
3.3
AO{
a: function(){},
b: undefined,
d: function(){}
}
执行结果
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);
以后遇到关于预编译的相关问题,大家就可以按照上面的步骤进行解决了,还有就是如果大家在遇到的代码里有if语句的话,在预编译阶段先无视他就可。希望这篇文章能够帮助到和我一样迷惑的你!