在学习js时,总是会遇到关于状态提升等一系列问题,那么为什么呢?为什么js引擎没有按照我们编写的顺序逐行解析呢?变量是怎么提升的呢?这个时候我们就需要去了解一下js代码的执行过程…
简单理解js的执行过程我觉得大致分为六步:
1、初始化全局对象 ;
2、构建全局执行上下文;
3、全局执行上下文放入执行上下文栈;
4、给全局对象赋值;
5、创建函数执行上下文;
6、依次出栈;
以执行下面js代码为例:
var num1 = 1;
console.log(test2)
var num2 = 2;
function fun1 (){
var funNum1 = 3;
function fun2 (){
console.log(funNum1)
}
fun2()
}
fun1 ();
一、初始化全局对象(G0)
js引擎会在执行代码之前,也就是解析代码时,会在我们的堆内存创建一个全局对象(简称GO),可以被所有作用域访问。
这个全局对象里面包含:
(1)一些全局的方法和类,像Math、Date、String、Array、setTimeout等等;
(2)一个指向全局对象自身的window属性;
(3)我们在代码中定义的全局变量,并把值设置为undefined(其中和普通数据有所区别的是,是,我们定义的函数存放的是地址,指向函数对象);
在上文提到的代码中,第一步就是在堆内存中创建一个全局对象。
全局对象存放上述三类数据,值得注意的是fun1是函数,所以会再堆内存中在开辟一块空间存放函数体,全局对象中存放这个函数的地址,指向开辟的这个函数体。
二、构建一个全局执行上下文(GEC)
执行上下文就是当前 JS 代码被解析和执行时所处环境的抽象概念,JS 中任何代码都是在执行上下文中运行的,分为全局执行上下文和函数执行上下文,一个程序中只存在一个全局执行上下文,可存在多个函数执行上下文。
初始化全局对象后就会构建一个全局执行上下文用于执行全局代码,全局执行上下文是最外层的执行上下文,任何不在函数中的代码都位于全局执行上下文,也就是在这个过程中把我们在代码中定义的全局变量拿到,并把值设置为undefined,造成了文章开头提到的状态提升的问题。
在代码执行前会把变量对象的内存地址指向全局对象。
此时为上述代码生成了一个执行环境
三、将全局执行上下文(GEC)放入执行上下文栈(ECS)中
js执行过程中会创建很多个执行上下文,执行上下文栈是用于存放执行上下文的,全局上下文构建完成后会被压入执行上下文栈的栈底。
此时把全局执行上下文压入栈底
四、给全局对象赋值
执行上下文栈创建完成之后就开始自上而下的执行js代码,依次对全局变量中的GO对象赋值。
开始逐步执行js代码
var num1 = 1; //此时全局对象中的num被赋值为1
console.log(num2)//这一步想要输出num2,输出全局对象中的num2的值undefined(也就是变量提升无值)
var num2 = 2;//这一步把全局对象中的num2赋值为2
function fun1 (){
var funNum1 = 3;
function fun2 (){
console.log(funNum1)
}
fun2()
}
fun1 ();//这里开始执行函数,也就是创建了函数上下文,下面会说到
五、创建函数上下文(FEC)
当js代码自上而下执行的时候,遇到了函数就会创建函数想下文,依次压入执行上下文栈。解析函数时会创建一个激活对象(AO)存储函数内的一些变量。
代码运行到函数fun1会生成它的激活对虾,进而创建fun1的函数上下文,然后把fun1上下文压入执行上下文栈。运行函数内部又遇到了fun2,然后又会重复上述操作,生成fun2的激活对象,创建fun2的上下文,压入栈中。
六、依次出栈
运行结束,依次出栈。