JS代码执行的两个阶段
- 预编译阶段:会将用var关键字声明的全局变量的
变量声明
和function关键字声明的全局函数的函数定义
提升 到代码的最前面,局部变量和局部函数会提升到该作用域的最前面 - 代码执行阶段:会产生执行上下文(Execute Context)
- var a=123
“var a”是声明 “=123” 是将number类型的123赋值给a 在这里简单认为定义=声明+赋值
通过一个简单的例子来了解 提升
1 console.log(a);
2 console.log(typeof a);
3 console.log(b);
4 console.log(typeof b);
5 var a =110;
6 function b(){
7 console.log("b......");
8 }
9 console.log(a);
10 console.log(typeof a);
- 运行结果:
- undefined:未定义
- 因为代码执行之前先将var声明的 变量声明 提升,所以执行第一、二行代码时,
变量a还没被赋值,所以浏览器给初始化为undefined (数据类型,值也为undefined)- 接下俩按顺序执行第三第四行代码,因为function声明的函数提升的是 函数定义
所以会将函数体一起提升上去,所以第三行能输出函数体第四行输出的时b
的数据类型- 执行第五行代码会给将 number类型的
110
赋值给a
函数因未被调用所以不执行- 执行第九第十行代码,因为
a
已经被赋值了所以输出结果会分别时110
和number
说明:这里解释的很粗浅,本文主要记录执行上下文,需要对提升
有简单的认识。
JS代码执行上下文
简单了解下数据的存储
内存分堆内存和栈内存
在js中,基本数据类型存储在栈中,引用数据类型存储在堆中。
简单了解一下EC
- EC的作用:给代码提供数据。
- 代码分两类:全局代码和函数代码
- 全局执行上下文在执行时,会产生全局的EC。这里简称EC(G)
- 函数代码执行时,产生一个局部的EC,调用一个函数就会产生一个EC
当函数调用完成后会将该EC销毁
接下来通过一个简单的例子来了解代码执行的一个过程(代码执行过程中还遵循作用域
和优先级
规则)。
1 console.log(a);
2 console.log(b);
3 var a =110;
4 function b()
5 {
6 console.log("b......");
7 var a=10;
8 c=123;
9 console.log(a);
10 }
11 b();
12 console.log(a);
13 console.log(c);
在代码执行阶段
第一步:产生了一个EC,并对全局变量进行提升如图
产生一个EC栈里面放入一个EC(G)(是全局EC)在全局EC中存放代码中的引用对象(VO),由于代码先提升,所以此时的
a
的值是一个undifined,而函数、数组引用对象的数据存放到堆
中,VO里面存储的是堆的内存地址
window是一个全局对象 只要是一个全局变量或全局函数都会挂载到window上,我们设置了一个GO(global object) ,`
-
执行代码阶段:一行一行执行代码
- 执行第一行代码,在控制台输出
a
的值,在VO中存在a
此时a
的值为undifined - 执行第二行代码,在VO中有函数
b
,将函数b
对应地址的内存空间中的函数体输出在控制台 - 执行第三行代码,将110赋值给变量
a
,此时VO中的a
的值更新为110
- 第四到十行代码是函数定义,还没调用,此时不执行。
-执行第十一行代码,调用函数b
,此时,新产生一个EC(b())进EC栈 - 进栈后,先将函数赋值,没有赋值,则开始提升,将
var a
提升到函数内部的最上面 - 开始执行第六行代码,在控制台输出字符串
b......
- 执行第一行代码,在控制台输出
- 执行第七行代码,将10赋值给a,由于AO中存在a 所以将10赋值给函数内部的a,而不是全局变量a
- 执行第八行代码,将数值123赋值给c,由于在AO中没有c,将从父EC中找,父EC中也没有,则将c=123存储在GO中
执行第九行代码,由于函数内部也有变量a,因此此处将10输出到控制台,
执行完函数,EC(b)将出栈,并销毁,内部存储的数据也将销毁
执行第12行代码,在VO中存在变量a,将VO中的a的值110输出到控制台
执行第13行代码,在VO中不存在变量c,则从GO中找,GO中存在变量c,将GO中变量c的值输出到控制台。
运行结束,EC栈并不会被销毁,当将页面关闭时,EC栈将销毁。