JS引擎:
- 执行JS代码的程序。前任。谷歌的 V8。
- V8 为 Chrome、Node.js 提供支持
- 其他浏览器有自己的 JS 引擎。
JS 引擎有 2 个部分:
- 调用堆栈:使用执行上下文执行我们的代码的地方。
- 堆:非结构化内存池,用于存储对象。
每个c/r程序都需要转换为机器代码。通过两个过程完成:
1.编译:将整个代码一次性转换为机器代码,写入计算机可以执行的二进制文件。
源代码 -(已编译)->二进制代码[可移植文件] -(已执行)-> PGM 运行
执行可以在编译后发生。
2.解释:解释器遍历源代码,逐行执行。
源代码-(逐行执行)-> PGM 运行
- 这里代码还需要转换为机器码。
- 与编译的 l/gs 相比,它们要慢得多。
3. JIT 即即时编译:
- 现代 JS 引擎是编译和解释的结合,使其速度更快。
- 整个代码立即转换为机器代码,然后立即执行。
源代码-(编译)->机器码-(执行)-> PGM 运行
- 没有要执行的中间可移植文件。
- 编译后立即执行。
- 因此,由于这种技术,JS 现在比解释的 l/gs 快得多。
JS中的编译过程:
步骤1.解析:
- 我们的代码被解析,即由 JS 引擎读取为 AST 或抽象语法树。
- 根据 const、let、function 等对 l/g 有意义的关键字将代码拆分为树。
- 然后将代码以结构化的方式保存到树中。
- 还要检查是否有任何语法错误。
- AST 与 DOM 树无关。 AST 只是 JS 引擎内代码的表示。
步骤2、3[组合]:编译+执行
- AST 会使用 JIT 进行编译并立即执行。
- 执行发生在调用堆栈中。
- 现代JS有巧妙的优化策略,即在开始时快速创建一个未优化版本的机器代码,以便尽快开始执行。
- 在后台,此代码在已运行的 pgm 执行期间再次重新编译。
- 通过多次迭代完成,每次优化后,未优化的代码都会与新优化的代码交换,而不会停止代码执行。这使得 V8 如此之快。
- 所有这些解析、编译、执行都发生在 JS Engine 内的某个特殊线程中,我们无法使用与使用调用堆栈运行代码的主线程完全分开的代码来访问该线程。
- JS 不再只是解释 l/g。它具有 JIT 编译,这使得它比解释的 l/gs 更快。
JS 运行时 = JS 引擎 + Web API + C/B 队列
- JS Runtime:包含我们使用 JS 所需的所有东西的容器。
- 任何 JS 运行时的核心都是 JS Engine。
- 没有 JS Engine,就没有运行时,因此根本就没有 JS。
- 仅 JS 引擎是不够的,我们需要访问 DOM、Fetch、Timers 等 Web API。
- Web API:运行时向引擎提供的功能,但不是 JS 引擎的一部分。前任。浏览器中的窗口对象,节点中的全局对象。
- 回调队列是一个包含所有准备执行的函数的数据结构。前任。点击、计时器、数据等
- DOM 事件处理函数 fns 也称为回调 fns。
- 当Call stack为空时,回调fn从C/B队列转移到Call stack执行。
- 持续的检查和移位是由事件循环执行的。
- 事件循环是让 JS 拥有非阻塞并发模型的东西。
- 对于node,我们没有浏览器提供的Web API。我们有一个叫做 C++ 绑定和线程池的东西。
JS 代码如何在调用堆栈上执行
- JS 有单线程执行,因此一次只能做一件事。因此,JS 中没有多重询问。
- API是由环境提供的,而不是语言的一部分。前任。 Web API,如计时器、Fetch、DOM、地理位置等
- 回调队列:准备执行回调函数,附加到已发生的某个事件。
- 每当调用堆栈为空时,事件循环就会将回调从回调传输到队列,然后再调用堆栈来执行。
- 因此,事件循环是使 JS 中的异步行为成为可能的重要部分。
- 并发模型:l/g 如何处理同时发生的多个事件。
- JS Runtime 的基本部分:
- 调用堆栈
- Web API
- 回调队列
- 事件循环
- 与 DOM 相关的所有内容都是 Web API 的一部分,而不是 JS。
- 图像加载以异步方式进行,如果以同步方式加载,那么它会被阻塞,即不在主线程上,而是在 Web API 环境上。
- 所有事件监听器、.then() 等工作都发生在 WEb API 环境中,而不是在调用堆栈上。
- 回调函数被放置在回调队列中等待它们在调用堆栈上执行。
- 回调队列就像一个调用堆栈必须完成的待办事项列表。
- 这里提到的持续时间是执行前的最小延迟,而不是执行的时间。
- 回调队列还包含来自 DOM 事件、点击、按键等的回调。DOM 事件不是异步行为,但它们使用回调队列来执行。
- 事件循环不断检查回调队列直到其为空。放置在调用堆栈上的每个回调都称为事件循环标记。
- 事件循环协调整个 JS 运行时。
- JS 本身没有时间概念,因为异步代码不在引擎中执行。运行时管理异步行为,事件循环决定执行哪个回调。
- 引擎或调用堆栈只是执行给它的代码。
- 当要加载图像时,事件侦听器会在 Web API 环境中持续等待,直到触发加载事件。当它被触发时,只有它作为回调 fn 进入回调队列,等待轮到它在调用堆栈上执行。
微任务队列:
- 来自 Promise 的回调不会进入回调队列,而是进入微任务队列。
- 该队列的优先级高于回调队列。
- 事件循环首先检查此队列,先执行其所有任务,然后转到回调队列执行。
- promise 的回调称为微任务,因此称为微任务队列。还有其他微任务,但目前与此处无关。 事件循环决定每个回调何时执行。与回调队列相比,它为微任务提供了更高的优先级
- 微任务可以在所有其他常规回调队列之前进行内联切割。
- Promise.resolve() :创建一个立即解决的 Promise,其成功值作为参数传递到其中。 then() 回调被调用,并以解析值作为参数。
console.log("Direct simple console BEGIN");
setTimeout(() => console.log("Text from Timer"),0);
Promise.resolve("Text from Promise").then(res => console.log(res));
console.log("Direct simple console END");
Order of Output:
Direct simple console BEGIN
Direct simple console END
Text from Promise
undefined
Text from Timer
登录后复制
- 如果微任务队列中有大量微任务或耗时的微任务,微任务队列甚至可能会导致回调队列挨饿。
console.log("Direct simple console BEGIN");
setTimeout(() => console.log("Text from Timer"),0);
Promise.resolve("Text from Promise1").then(res => console.log(res));
Promise.resolve("Text from Promise2").then(res => {
for(let i=0; i<5000; i++)
console.log(res);
});
console.log("Direct simple console END");
登录后复制
以上是JavaScript引擎的详细内容。更多信息请关注PHP中文网其他相关文章!