javaScript 执行顺序
代码经过编译后,会生成两部分内容:执行上下文
和可执行代码
。区分就是代码是定义东西还是执行东西。
执行上下文是 JavaScript 执行一段代码时的运行环境
,比如调用一个函数,就会进入这个函数的执行上下文,确定该函数在执行期间用到的诸如 this、变量、对象以及函数等。
执行上下文中存在一个变量环境的对象,保存了变量提升的内容,如变量和定义的函数。
egg:
showName()
var showName = function() {
console.log(2)
}
showName()
function showName() {
console.log(1)
}
编译部分:
var showName = undefined
function showName() {
console.log(1)
}
可执行部分:
showName()
showName()
最终执行流程:
showName() 首先被调用,查找变量环境中该函数,找到两个同名函数,第一个值为undefined,第二个为log(1),按顺序于是第二个覆盖第一个,
执行输出1
。
第二个showName() 被调用,再去变量环境查找该函数,此时 由于执行了 var showName = function() {log(2)} 赋值操作,变量环境的showName函数又被覆盖了,log(2),覆盖原来的log(1),
执行输出log(2)
javaScript 的调用栈
通常把这种用来管理执行上下文的栈称为执行上下文栈,又称调用栈。
- 每调用一个函数,JavaScript 引擎会先对其编译,创建执行上下文,把该执行上下文入栈,然后 JavaScript 引擎再执行其可执行代码。
- 如果在一个函数 A 中调用了另外一个函数 B,那么 JavaScript 引擎会为 B 函数创建执行上下文,并将 B 函数的执行上下文压入栈顶,在执行B中可执行代码。
- 当前函数执行完毕后,JavaScript 引擎会将该函数的执行上下文弹出栈。
- 当分配的调用栈空间被占满时,会引发“堆栈溢出”问题。
结合题目理解:
var a = 2
function add(){
var b = 10
return a+b
}
add()
执行上下文准备好之后,便开始执行代码。
- 首先,从全局执行上下文中,取出 add 函数代码。
- 其次,对 add 函数的这段代码进行编译,并创建该函数的执行上下文和可执行代码。
- 最后,执行代码,输出结果。
看段稍微复杂点的示例代码:
var a = 2
function add(b,c){
return b+c
}
function addAll(b,c){
var d = 10
result = add(b,c)
return a+result+d
}
addAll(3,6)
-
首先全局代码编译,创建全局上下文,变量提升
-
全局代码编译完,就开始执行全局可执行代码:a = 2 ,addAll(3,6)
-
addAll(3,6)函数调用,JavaScript 引擎会编译该函数,创建一个局部执行上下文,变量提升,将该函数的上下文入栈
-
addAll 函数编译部分完成后,执行可执行函数, d = 10,add(b,c) 调用结果赋值给 result
-
add(b,c)函数调用,同样会编译,创建执行上下文,并将其压入调用栈
-
紧接着执行add函数可执行部分, 从参数列表获取变量值后,return 9,该函数上下文出栈,接着addAll剩余的可执行部分
return a + result + d
执行, addAll 的执行上下文也会从栈顶部弹出,最后只留全局上下文在stack中,至此,整个 JavaScript 流程执行结束了。
调用栈是 JavaScript 引擎追踪函数执行
的一个机制,当一次有多个函数被调用时,通过调用栈就能够追踪到哪个函数正在被执行以及各函数之间调用关系
。
console.trace() 输出当前的函数调用关系,比如在示例代码中的 add 函数里面加上了 console.trace(),你就可以看到控制台输出的结果,如下图:全局 > addAll > add