全局环境:JavaScript代码运行起来会首先进入该环境
函数环境:当函数被调用执行时,会进入当前函数中执行代码
eval(不建议使用,可忽略)
函数调用栈(call stack)
因此在一个JavaScript程序中,必定会产生多个执行上下文,在我的上一篇文章中也有提到,JavaScript引擎会以栈的方式来处理它们,栈底永远都是全局上下文,而栈顶就是当前正在执行的上下文。
单线程
同步执行,只有栈顶的上下文处于执行中,其他上下文需要等待
全局上下文只有唯一的一个,它在浏览器关闭时出栈
函数的执行上下文的个数没有限制
每次某个函数被调用,就会有个新的执行上下文为其创建,即使是调用的自身函数,也是如此。
eg:
Event Loop 是一个很重要的概念,指的是计算机系统的一种运行机制。
一个负责程序本身的运行,称为"主线程";另一个负责主线程与其他进程(主要是各种I/O操作)的通信,被称为"Event Loop线程"(可以译为"消息线程")。这种运行方式称为"异步模式"(asynchronous I/O)或"非堵塞模式"(non-blocking mode)。
同步和异步任务分别进入不同的执行"场所",同步的进入主线程,异步的进入Event Table并注册函数。
当指定的事情完成时,Event Table会将这个函数移入Event Queue。
主线程内的任务执行完毕为空,会去Event Queue读取对应的函数,进入主线程执行。
上述过程会不断重复,也就是常说的Event Loop(事件循环)。
广义的同步任务和异步任务,我们对任务有更精细的定义:
macro-task(宏任务):包括整体代码script,setTimeout,setInterval
micro-task(微任务):Promise,process.nextTick
例如:setTimeout(()=> { task()},3000);sleep(10000000);
task()进入Event Table并注册,计时开始。
执行sleep函数,很慢,非常慢,计时仍在继续。
3秒到了,计时事件timeout完成,task()进入Event Queue,但是sleep也太慢了吧,还没执行完,只好等着。
sleep终于执行完了,task()终于从Event Queue进入了主线程执行
宏任务Event Queue setTimeout1 setTimeout2
微任务Event Queue process1 then1
一轮宏任务执行:整体script主线程,遇到console.log,输出1。new Promise直接执行,输出7。process1和then1两个微任务。输出6,8
二轮宏任务执行:setTimeout1宏任务开始:先执行输出.2,4 ,process2和then2 两个微任务可以执行,输出3,5。
二轮宏任务执行:setTimeout2宏任务开始:先执行输出 9,11,process和then 两个微任务可以执行,输出10,12