一、前言
本篇文章的目的是尽可能的解释清楚在js中的代码执行过程,通过三个阶段着手:
代码执行前的准备代码执行过程总结
二、执行前的准备
代码执行前分为两个阶段:
语法分析:检查代码语法是否存在错误预编译:创建执行上下文
语法分析
检查代码词法与语法有无错误,如有错误打印错误并停止执行。
预编译(解析声明创建执行上下文)
GO执行上下文: 执行上下文分GO(全局执行上下文)和AO(函数执行上下文)两种,预编译初始化时只创建GO变量提升:
let c
= 3
console
.log(add(1, 2))
console
.log(add2
)
console
.log(a
)
console
.log(b
)
var a
= 1
var add2
= add
function add(num1
, num2
) {
return num1
+ num2
+ c
}
c
= 3
console
.log(add(1, 2))
console
.log(add2
)
console
.log(a
)
console
.log(b
)
a
= 1
add2
= add
执行以上代码执行过程分析:
解析function add方法声明,将add方法放入全局执行上下文中;解析var a, var add2,将变量a, add2放入全局执行上下文中,并赋值undefined开始执行代码,执行到console.log(add(1, 2)),创建函数执行上下文,并将变量num1, num2放入上下文中执行到console.log(b),发现上下文中未声明该变量,停止执行代码并抛出错误以上过程,在预编译阶段将变量声明保存到执行上下文中的过程就叫做变量提升
执行过程变量示意图:
三、代码执行过程
AO执行上下文: 函数初次调用时才会创建AO,但是如果实现了闭包,那么AO会被保存到堆内存中,等待合适时机调用。(闭包导致内存泄漏的病根)调用栈
const step
= 1
function add(num1
, num2
) {
console
.log('add step:' + step
)
return num1
+ num2
}
function addStep(num
) {
const step
= 5
console
.log('addStep setp:' + step
)
return add(num
, step
)
}
console
.log('global setp:' + step
)
console
.log('addStep sum:' + addStep(123))
代码执行调用栈分析:
创建全局执行上下文,压入栈中创建addStep函数执行上下文,压入栈中创建add函数执行上下文,压入栈中add函数执行完毕,add出栈addStep执行完毕,addStep出栈打印结果,清空调用栈,退出任务
调用栈状态图
调用栈是在代码运行过程中,保存函数调用状态的一种数据结构,类似队列,拥有先进后出的特点,可以在代码执行过程中,快速定位代码执行过程的位置。
语句级别执行
非控制语句
声明类语句表达式语句debugger空语句 控制语句
try…catch(块级)for, for…in, for…of等(块级)while…(块级)if…else(块级)switch…case(块级)return(功能语句)break(功能语句)continue(功能语句)throw(功能语句)
语句的执行过程中,每一个语句执行完毕,都会返回一个特定的标志,以供javascript引擎判断是否继续执行下一行语句。不同的块级,会对不同的标志有特定的反馈。比如,function块级会对return做出退出的反馈,if也会对return做出停止执行的反馈。while会对break做出退出的反应,对continue做出跳出当此循环的反馈等等。而不同的功能语句,执行时会返回不同的标志。例如:
非控制语句: 返回normal(继续执行标志)return: 返回returnbreak: 返回breakcontinue: 返回continuethrow: 返回throw
不同的块级语句创建的语句块会对不同的标志进行反馈,如下:
语句returnbreakcontinuethrow
if穿透穿透穿透穿透switch反馈穿透穿透穿透for/while反馈反馈穿透穿透function报错报错反馈穿透try特殊处理特殊处理特殊处理反馈catch特殊处理特殊处理特殊处理穿透finally特殊处理特殊处理特殊处理穿透
在此借用winter老师总结的表格。
四、总结
以上就是分享的本人对javascript代码执行过程的理解,谢谢~