众所周知,javaScript是解释性语言,其特点就是解释一行执行一行,所以js运行过程可以简单总结为3步:1.语法分析 2.预编译 3.解释执行
- 语法分析就是会在代码执行前对代码进行通篇扫描检查,从而发现排查一些低级错误;
- 预编译发生在代码执行的前一刻,会完成函数声明的提升和变量声明的提升;
- 解释执行就是执行代码
今天我们就一起来扒一下,预编译过程到底干了什么吧:
首先,我们需要知道:
- 预编译分为全局预编译和局部预编译,全局预编译发生在页面加载完成时执行,而局部预编译发生在函数执行的前一刻;
- 预编译阶段主要是函数声明和变量声明的提升,没有初始化行为(赋值),同时匿名函数不参与预编译 。只有在解释执行阶段才会进行变量初始化;
- JavaScript的执行过程会先扫描一下整体语法语句,如果存在逻辑错误或者语法错误,那么直接报错,程序停止执行,没有错误的话,开始从上到下解释一行执行一行;
所以了解预编译的过程,则可以更加清晰明了js代码的执行过程。
全局预编译步骤
- 创建Global Object全局对象, 下文简称GO;
- 找变量声明,将变量名作为GO属性名,值为undefined;
- 查找函数声明,作为GO属性,值赋予函数体。
局部预编译步骤
- 创建Activation Object 执行期上下文,下文简称AO;
- 找形参和变量声明,将变量和形参名作为AO属性名,值为undefined;
- 将实参值和形参统一;
- 在函数体里面找函数声明,作为AO属性,值赋予函数体;
根据以上步骤,可以分析出下面这段代码的打印信息
console.log(global)
global = 100;
console.log(global)
function test(a){
console.log(global);
var global = 200;
console.log(global);
console.log(a);
var a = 123;
console.log(a);
function a(){};
console.log(a);
console.log(b)
var b = function(){};
console.log(b);
console.log(d);
// 函数
function d(){};
console.log(d);
}
test(1);
var global;
看到这,你一定对打印信息有了自己的答案~
下面我们一起来分析一下这段代码的全局预编译和局部预编译以及执行过程吧~
//浏览器加载到这段代码,会对这段代码进行一个全局的预编译:
//此时的GO对象为:
//GO: {
// global: undefined
// test: function () {}
//}
console.log(global)//undefined,虽然global变量的声明语句在代码底部,但是预编译时已经被提升,只是此时的AO对象中global还没被赋值,所以打印还是个undefined
global = 100;
//执行到这,此时的GO对象为::
//GO: {
// global: 100
// test: function () {}
//}
console.log(global) //100
function test(a){ //这只是test函数声明
//执行test函数,会对test函数进行局部预编译,生成test函数的AO对象:
//一开始会对变量和函数的声明进行提升,此时的 test A0对象为
// test AO: {
// global: undefined //因为函数内也定义了global变量,预编译时,函数会提升自己AO里的变量
// a: function a() {} //局部作用域中有变量a也有函数a,因为函数提升在变量提升之后,所以现在AO中的a是一个function
// b: undefined //因为b是函数表达式,只对变量进行提升,所以此时的b是undefined
// d: function d () {}
// }
console.log(global);//undefined
// 声明 + 赋值
var global = 200;
console.log(global);//200
console.log(a); //function a() {} ,因为预编译时函数a的声明被提升,所以现在a是函数
var a = 123; // 执行到这时,由于变量赋值是不提升的,所以函数被123覆盖了
console.log(a); // 123
// 函数声明
function a(){}; //在这只是函数声明,前面已经被提升了,所以再打印a,a还是123
console.log(a); //123
console.log(b) //undefined
// 函数表达式
var b = function(){}; //赋值
console.log(b); //function(){};
console.log(d); //function d(){}
// 函数
function d(){};
console.log(d); //function d(){}
}
//调用test函数
test(1); //代码执行到这,则开始执行test函数,执行test函数的时候会生产test函数的AO对象
var global; //全局global变量的声明
浏览器最终打印结果:
如图所示,浏览器最终的打印结果,和我们分析的一致。那你现在对js的预编译过程熟悉和了解了吗?
总结:
对变量和函数的声明进行提升;
2.全局的GO里和函数的AO里有一样的变量的时候,执行函数时,函数会优先使用自己的AO里的变量值。
3.被提升的变量和函数声明,在代码执行的时候就不再被执行了因为已经被提升了,但要注意函数表达式和函数声明,函数表达式提升的是变量。(例如:var b = function () {} 被提升的是变量b,预编译的时候b是undefined,执行到var b = function的时候,b才是function)
拓展:
- 任何变量,如果变量未经声明就赋值,此变量就为全局对象所有。
2.一切声明的全局变量,全是window的属性。