⼏乎每个⼈都听说过V8引擎这个概念,并且每个⼈都知道JavaScript是单线程或者它使⽤⼀个callback队列。
在这篇⽂章中,我将要深⼊这些细节并且解释JavaScript是如何运⾏的。通过了解这些,有助于帮你写出更好且⽆阻塞的应⽤。
如果你对JavaScript了解尚少,这篇⽂章将要帮你理解为什么JavaScript会显得如此“与众不同”。⽽如果你是⼀位有经验的开发者,这篇⽂章将要给你不同的视⻆理解JavaScript。
JavaScript 引擎
最为⼈熟知的JavaScript引擎是Google V8。它现在是⽤在Chrome和Node内部。下⾯是⼀个很简单的概述图:
通过以上视图,可得知JavaScript引擎包含两个组件:
Memory Heap -- 分配内存的地⽅
Call Stack -- 执⾏代码时,存放栈帧的地⽅
Runtime
⼀些JavaScript开发者使⽤了⼀些“特殊”的api (⽐如setTimeout)。但是这些api却不是由引擎提供的。⽽它们的来历有点复杂。
⽐如DOM、AJAX、setTimeout等是由浏览器提供的,我们称之为WEB APIs。
3. JavaScript引擎和Runtime的区别
⾸先在某些情况下,这两个概念不是明确的,有时甚⾄是可以互换的。
从概念上讲engine负责解析和JIT编译,例如: 把JavaScript中的语⾔编译成机器码。⽽ runtime提供内建的库,可以在程序运⾏时使⽤。所以可以在浏览器中使⽤Window 对象或者DOM API,这些存在于浏览器的runtime。⽽node runtime包含不同的库,⽐如Cluster 和FileSystem API。两个runtime都包含内置的数据类型和常⽤的⼯具,⽐如Console对象。因此Chrome和Node.js共享相同的引擎(V8),但是它们具有不同的Runtime。
Call Stack
JavaScript是单线程,只有⼀个Call Stack。意味着只能在同⼀时间做⼀件事情。Call Stack 是⼀个记录程序运⾏到确定位置的数据结构。⽐如执⾏到某个函数时,函数⼊栈。⽽执⾏完后,函数出栈。这就是Call Stack所要做的⼯作,让我们看看以下例⼦:
开始阶段,Call Stack 是空的。后来,按照以下流程运⾏
stack frame (栈帧): 调⽤栈中的每个条⽬(包括该函数本身,函数运⾏的变量)
在发⽣异常时,Call Stack 运⾏流程可以被打印出来,运⾏以下代码(foo.js)
栈溢出-- 当栈帧数量达到Call Stack最⼤值时就会发⽣。⽽栈溢出是很容易发⽣的,执⾏以下代码
以上函数递归调⽤⾃⼰没有终⽌条件,每⼀次执⾏foo函数,Call Stack就会添加⼀个栈帧。这就像下⾯显示的那样:
当栈帧超过 Call Stack 最⼤限制,浏览器会通过抛出错误终⽌程序,⽐如以下
在单线程上运⾏代码是轻松的,因为不必考虑复杂的场景,⽐如由多线程情况引发的死锁。
但是运⾏单线程也是有缺陷的,因为javascript仅仅有⼀个Call Stack。当处理耗时业务时应该怎么办呢,总不能⼀直等待被处理吧。这时就需要异步。通过EventLoop和Callback Queue构成的异步处理效率就会⼤⼤提⾼。
How JavaScript works: an overview of the engine, the runtime, and the call stack What-is-the-difference-between-javascript-engine-and-javascript-runti
领取专属 10元无门槛券
私享最新 技术干货