目录
引入问题
JS代码执行顺序有时与代码先后顺序有所差异,与预期是有差别的,例如下面的例子函数声明和函数表达式两者执行就不相同
//函数声明
function f1() {
console.log('我是f1+1');
};
f1(); //我是f1+2
function f1() {
console.log('我是f1+2');
};
f1(); //我是f1+2
//函数表达式
var f1 = function () {
console.log('我是f1+1');
};
f1(); //我是f1+1
var f1 = function() {
console.log('我是f1+2');
};
f1(); //我是f1+2
注:
- 函数声明和函数表达式的区别
- 函数声明
- JS中有变量被提升的机制,即变量函数声明会被提升到作用域最前面
- 当是函数声明时,函数调用可在函数声明的父级作用域中函数生命前调用,可以正常调用函数
- 函数表达式
- 函数表达式无提升
- 当在表达式前面进行调用时,则不能正常调用
- 函数声明
从上例中易知代码执行时发生变化,在代码执行前JS引擎会先创建对应的执行上下文
执行上下文是什么
执行上下文是对JS代码执行环境的抽象概念,即只要有JS代码在运行,则一定是在执行上下文中进行运行。
扫描二维码关注公众号,回复:
14300486 查看本文章
执行上下文的分类
- 全局执行上下文:即浏览器中的全局对象是window对象,this指向的是这个全局。即有一个
- 函数执行上下文:当函数被调用的时候被创建,每次调用函数会创建一个新的执行上下文。即有无数个(这么多的执行上下文是使用栈管理,下文中解释栈的使用)
- Eval函数执行上下文:是eval函数中的代码,很少使用
注解:
- 调用函数创建的新的上下文会有一个私有作用域,函数内部声明的任何变量都不能在这个函数作用外进行访问,只能在函数内部进行访问
生命周期
执行上下文的生命周期包括阶段: 创建阶段、执行阶段、回收阶段
创建阶段
- 确定this的值(This Binding)
- 在全局执行上下文时,this总是指向全局对象。
- 函数执行上下文中,this值取决于函数调用方式,被对象调用则指向对象,否则一般指向全局对象windows或undefined
- 语法环境(LexicalEnvironment)组件被创建 -- 两个组成:全局环境(this指的是全局对象)、函数环境
- 变量环境(VariableEnvironment)组件被创建
变量提升的主要原因:--创建阶段时var生命的变量赋值undefined值
创建阶段对于var、let、const的赋值状态: let和const定义变量在创建阶段没有被赋值,var生命的变量在从创建阶段被赋值undefined。 -- 原因:在创建阶段,代码处扫描变量和函数声明,然后将函数声明存储在环境中,单变量会被初始化成undefined(var声明时)和保持初始状态(let、const声明时)
执行阶段
- 执行变量赋值
- 代码执行
注解:当JS引擎源代码中找不到变量的值,将分配undefined值
回收阶段
- 执行上下文出栈
- 等待虚拟机回收上下文
执行栈
执行栈,也为调用栈,先进后出结构,用于存储代码执行期间创建的所有执行上下文
执行顺序:
- JS引擎开始执行第一行脚本代码,先创建一个全局执行上下文之后将它压倒执行栈中。
- 每当引擎遇到一个函数时,会创建一个函数执行上下文,然后将这个和执行上下文压到执行栈中。
- 引擎会执行位于执行栈栈顶的执行上下文(一般是函数执行上下文),该函数执行结束后,对应的执行上下文会被弹出,然后控制流程到达执行栈的下一个执行上下文
注:JS代码执行完毕前在执行栈底部永远有个全局执行上下文
转化成图的形式
流程:
- 创建全局上下文请压入执行栈
first
函数被调用,创建函数执行上下文并压入栈- 执行
first
函数过程遇到second
函数,再创建一个函数执行上下文并压入栈 second
函数执行完毕,对应的函数执行上下文被推出执行栈,执行下一个执行上下文first
函数first
函数执行完毕,对应的函数执行上下文也被推出栈中,然后执行全局上下文- 所有代码执行完毕,全局上下文也会被推出栈中,程序结束