一、前言
js的预编译,是js的基础内容,学好预编译,很多js开发问题都会迎刃而解。
js在浏览器环境中运行流程一般分为三步走:
- 语法分析(检查代码的语法错误)
- 预编译(初始化变量和函数)
- 代码执行
js在预编译会在代码执行前一刻执行,同时会创建对象“全局上下文GO”,遇到函数时会创建对象“函数上下文AO”。
二、暗示全局变量(imply global variable)
暗示全局变量是js预编译的特点,顾名思义,就是在js中,如果变量没有声明就赋值,该变量就会被默认成为全局变量。
代码:
function foo(){
a = 1;
var b = 2;
}
foo()
console.log(a); // 1
console.log(b); // 报错 b is not defined
解读:1、foo函数内部将a变量直接赋值,默认该变量为全局变量,所以在函数外部可以打印出a;
2、变量b为函数内部声明的变量,全局上下文无法访问到,所以会报错;
三、全局上下文对象 GO (global object)
js预编译开始时,会创建一个对象GO,等同于全局对象window,用来存放初始化全局对象和函数;
步骤:
- 创建GO
- 将var声明的变量名作为GO对象的属性名,值为undefinded;
- 将声明函数的函数明作为GO对象的属性名,值为函数体;
代码:
console.log(a); // undefinded
var a = 1;
console.log(a); // 1
console.log(foo); // ƒ foo(){}
function foo(){};
解读:根据预编译步骤可得
1、创建GO对象;
2、寻找var声明的变量,并赋值为undefinded
GO {
a:undefind,
}
3、寻找函数声明,并赋值为函数体
GO {
a:undefind,
foo:foo(){}
}
预编译结束,开始执行代码,
第一行 a输出为undefinded;
第二行 a赋值为1,更新GO{
a:undefinded---->1,
foo:foo(){}
}
第三行 a输出1;
第四行 foo输出 foo(){};
四、函数上下文对象AO(activation object)
在函数执行前执行函数预编译,此时会产生一个AO对象,AO对象保存该函数的参数及内部变量。
步骤:
1、创建AO对象
2、将函数的参数以及函数里面声明的变量当做AO对象的属性名,值全部为undefined。
3、将实参的值赋值给形参。
4、在函数里面声明的函数,函数名作为AO对象的属性名,值赋值给函数体。(若参数名和函数名重叠,则函数体值会覆盖参数值)
代码:
console.log(a); // undefinded
var a = 2;
foo(3);
function foo(b){
console.log(b) // ƒ b(){}
console.log(a); // undefinded
var a = 1;
console.log(a) // 1
function b(){}
}
console.log(a) // 2
解读:根据预编译步骤可得
1、创建GO对象;
2、寻找var声明的变量,并赋值为undefinded
GO{
a:undefinded
}
3、寻找函数声明,并赋值为函数体
GO{
a:undefinded
foo:foo(){}
}
开始执行代码
第一行 全局变量a输出 undefinded
第二行 全局变量a赋值为2
第三行 执行前 局部预编译 生成AO
开始局部预编译
1、创建AO对象;
2、将函数的参数以及函数里面声明的变量当做AO对象的属性名,值全部为undefined
AO{
b:undefinded,
a:undefinded
}
3、将实参的值赋值给形参
AO{
b:undefinded —> 3,
a:undefinded
}
4、在函数里面声明的函数,函数名作为AO对象的属性名,值赋值给函数体。(若参数名和函数名重叠,则函数体值会覆盖参数值)
AO{
b:undefinded —>3 —>b(){},
a:undefinded
}
局部预编译结束,继续执行代码
第五行 局部变量 b输出 b(){};
第六行 局部变量 a输出 undefinded;
第七行 局部变量 a 赋值1
AO{
b:undefinded —>3 —>b(){},
a:undefinded —>1
}
第八行 输出局部变量a为1;
第十一行 输出全局变量a为2;
结束。