Javascript是单线程单一并发语言,这意味着它可以一次处理一个任务或一次处理一段代码。它有一个单独的调用堆栈,它与堆之类的其他部分一起构成了Javascript并发模型(在V8内部实现)。我们首先来看看这些术语:
下面是我总结的一张图片:
这里我展开讲,如果需要详细了解请参阅下面参考网址。
下面以一道面试题展开:
1 | function Foo() { |
执行上下文
代码开始,引擎创建全局执行上下文:
创建阶段
- ThisBinding: 这边的
this
为window对象 - 词法环境:
- 环境记录:
- Foo:
- getName:
- Foo:
- 外部环境引用:
null
- 环境记录:
- 变量环境:
- 环境记录:
- getName: undefined
- 外部环境应用:
- 环境记录:
- ThisBinding: 这边的
正是由于javascript引擎会在执行上下文之前的创建阶段(即变量提升),所以代码实际上是变成如下:
1 | function Foo() { |
所以第一问的答案输出为4
。
- 执行阶段
调用堆栈
假设当开始执行到Foo().getName()
时,开始进入Foo
函数,把Foo
函数压入调用堆栈顶部。创建Foo
局部执行上下文创建阶段:
- ThisBinding: 由于这边没有对象调用,故为this为
window
对象 - 词法环境:
- 环境记录:
- 外部环境引用:
- 变量环境:
- 环境记录:
- getName: function(){ alert(4); }
- 外部环境应用:
- 环境记录:
return this
返回时,Foo
函数弹出栈顶,Foo
局部执行上下文销毁。
故执行时第二问答案输出1
;第三问因为getName
已被第二问中修改,故输出1
。
事件循环
事件循环是指主线程重复从消息队列中取消息、执行的过程。如果遇到同步函数,直接压入调用栈,如果异步代码,则委托给事件队列,然后继续运行代码,当异步处理返回时压入事件队列,主线程在执行完当前循环中的所有代码后,就会到消息队列取出这条消息,并执行它:
参考网址
- https://tylermcginnis.com/javascript-visualizer/?code=function%20Foo%28%29%20%7B%0A%20%20%20%20getName%20%3D%20function%20%28%29%20%7B%20alert%20%281%29%3B%20%7D%3B%0A%20%20%20%20return%20this%3B%0A%7D%0AFoo.getName%20%3D%20function%20%28%29%20%7B%20alert%20%282%29%3B%7D%3B%0AFoo.prototype.getName%20%3D%20function%20%28%29%20%7B%20alert%20%283%29%3B%7D%3B%0Avar%20getName%20%3D%20function%20%28%29%20%7B%20alert%20%284%29%3B%7D%3B%0Afunction%20getName%28%29%20%7B%20alert%20%285%29%3B%7D%0A%0A%2F%2F%E8%AF%B7%E5%86%99%E5%87%BA%E4%BB%A5%E4%B8%8B%E8%BE%93%E5%87%BA%E7%BB%93%E6%9E%9C%EF%BC%9A%0AgetName%28%29%3B%0AFoo%28%29.getName%28%29%3B%0AgetName%28%29%3B
- https://mp.weixin.qq.com/s/QljMmVPJ8k7eYB2ynV03LQ
- https://medium.com/@gaurav.pandvia/understanding-javascript-function-executions-tasks-event-loop-call-stack-more-part-1-5683dea1f5ec
- https://www.valentinog.com/blog/js-execution-context-call-stack/
- https://blog.bitsrc.io/understanding-execution-context-and-execution-stack-in-javascript-1c9ea8642dd0