变量提升
变量和函数声明在代码里的位置是不会改变的,而是在编译阶段被JavaScript引擎放在了内存中。
JavaScript代码在执行前会被JavaScript引擎编译,编译完成之后才会进入执行阶段。
流程: 一段JavaScript代码-编译阶段-执行阶段
showName();
console.log(myname);
var myname = 'juejin';
function showName(){
console.log(函数'showName'被执行);
}
一段JavaScript代码进入编译阶段:
- 会生成两部分:
- 执行上下文
- 变量环境的对象
- 保存了变量提升的内容
- 变量环境对象如何被创建的?
- 上面的代码遇到第三行声明,会在变量环境的对象中加入属性myname并初始化为undefined
- 遇到第四行函数定义会将函数定义存储到堆(HEAP)中,变量环境的对象中加入属性showName,属性值指向堆中的函数位置。
- 之后JavaScript引擎会声明以外的代码生成字节码,
- 词法环境
- 变量环境的对象
- 可执行代码
- 执行上下文
执行阶段
- 执行第一行,会在变量环境对象中查找showName,并执行
- 执行第二行,会在变量环境对象中查找myname,打印undefined
- 执行第三行,在比变量环境中将myname值改为'juejin'
总结:
- JavaScript代码在执行过程中会进行变量提升,之所以会有变量提升,是因为在执行之前会先编译
- 在编译阶段,变量和函数会被放入变量环境对象,变量会被初始化为undefined,在执行阶段JavaScript引擎会从变量环境对象中查找变量
- 如果声明了相同的变量,后面的变量声明会在变量环境对象中覆盖前面的
执行上下文
- 全局执行上下文
- 函数执行上下文、函数被调用的时候才会编译函数内的代码,创建函数执行上下文
var a = 2;
function add(){
var b = 10;
return a+b;
}
add();
代码执行过程:
- JavaScript代码编译阶段会创建全局执行山下文,全局执行上下文包含变量环境对象和词法环境。
- 变量环境中有:a=undefined、add方法
- 执行到add()时,JavaScript判断这是一个函数的调用,
- 从全局上下文中取出add函数
- 对add函数进行编译,创建该函数的执行上下文和可执行代码
- 执行代码,输出结果
在执行JavaScript代码的时候可能会有多个执行上下文,JavaScript引擎使用栈这种数据结构来管理。栈是后进先出的
调用栈
用来管理执行上下文的,执行上下文被创建后,JavaScript引擎将执行上下文压入栈中,这种栈被称为执行山下文栈,又称为调用栈
查看方法:
- 对代码打断点时可以从Chrome开发者工具中的Call Stack中查看。
- console.trace()输出当前函数的调用关系
var a = 2;
function add(b,c){
return b + c;
}
function addAll(b,c){
var d = 10;
result = add(b,c);
return a+result;
}
addAll(3,6);
- 变量a、add、addAll会被保存在全局执行上下文的变量环境对象中
- JavaScript引擎在编译阶段将全局执行上下文压入栈后,开始执行全局代码
- 执行a=2;将全局之行上下文中变量环境对象中的a=undefined改为a=2
- 调用addAll
- JavaScript引擎编译addAll函数,并为addAll创建函数执行上下文,包括函数执行上下文中的变量环境对象和词法环境,将addAll的函数执行上下文压入调用栈中
- 进入函数代码执行阶段,执行d=10,将addAll执行上下文中变量环境对象中d值改为10;
- 执行add函数,创建add函数执行上下文,并压入调用栈,当add函数返回时,add函数执行上下文从栈顶弹出,将addAll中变量环境对象的result设置为add函数的返回值9;执行return a+result;addAll函数执行上下文从栈顶弹出
- JavaScript执行流程结束
栈溢出
是因为调用栈是有数量限制的,递归函数如果没有终止条件,会一直创建函数执行上下文导致超出调用栈的数量。栈溢出。