JavaScript运行时环境

6/5/2023 JavaScript

请注意 JavaScriptJavaScript runtime environment 不是一回事

下文中 JavaScript 简称为 JS

原文在这里 (opens new window),针对原文有些删减和增加的部分

# 什么是JS引擎?

首先,JS 是一门 解释执行语言 。这意味着源代码在执行前,无需编译为二进制代码。

那么,问题来了,计算机如何读懂并运行这些纯文本的源代码呢?

这,就是 JS引擎 需要做的事了

JS 引擎是一个把源码翻译为机器码,并将机器码通过 CPU 来执行的程序

把 JS 引擎想象成一个容器,你的程序(代码)正运行在容器中

image-20230606181132314

每个浏览器都有一个 JS 引擎,主流的 JS 引擎 有哪些:

  • Chrome - V8,由谷歌浏览器退出,同时 Opera 浏览器,NodeJS 也在应用
  • SpiderMoney,开源引擎,由 Mozilla 基金会维护,应用于 Firefox 浏览器
  • Nitro,由苹果公司开发,应用于 Safari 浏览器
  • Chakra,由微软公司开发,应用于 IE 9 及以上的浏览器以及 Microsoft Edge 浏览器

对于 JS 引擎更深的知识,这里就不做更多的解释了

# 什么是 JS 运行时环境?

JS 引擎是运行在一个环境中的,这个环境提供了代码在执行的时候能利用的附加特性

JS 运行时环境,可以是浏览器,也可以是 Node 服务器

# JS 是单线程的么?

你也许听过 JS 是单线程执行的说法

其实,这并不完全正确

JS 代码是在单一线程中执行的,这是毋庸置疑的。但这并不代表 JS 运行时环境是在单一线程中工作的。JS 运行时是存在线程池的,幸运的是,你不需要为线程调度操心,因为 JS 运行时环境会为你安排

但是,JS 运行时环境是怎么知道什么时候该执行什么代码呢?如果只有单一线程负责代码执行,那么一定存在某种机制来管理执行的顺序。

这种机制就叫 事件循环(event loop) (opens new window)

# 什么是 JS 事件循环?

在回答什么是事件循环之前,你需要知晓一个重要的细节。所有 JS 编写的代码都能够分为两大类

第一类是 立即调用代码 ,一旦代码载入环境,会通过 JS 引擎执行。初始化脚本通常是在网页加载完成后被立即调用的。

第二类是 事件回调,一个事件回调是当特殊事件被触发时,执行的一段代码。例如:一次鼠标点击事件,一此网络请求的应答。

事件循环,作为 JS 运行时环境的一部分,是一种专门处理回调的机制

当特定事件发生时,运行时环境会将相关回调推入一个所谓的事件处理队列。事件循环机制会持续监控队列,并且按照先来后到的顺序执行其中的回调。

那么,事件循环是怎么处理执行的顺序呢?

# 微任务与宏任务

当运行脚本时,遇到微任务或者宏任务时,分别将它们排到微任务队列和宏任务队列中,事件队列是先进先出,当 JS 本轮宏任务执行完后会先看微任务队列中有没有微任务,如果有的话先执行微任务,当微任务执行完后,会执行宏任务队列中的宏任务

微任务和宏任务执行的优先级是,微任务优先,浏览器在执行每一个宏任务后都会先看微任务队列中有没有微任务,当把微任务队列中的微任务执行完成后,才会执行下一个宏任务

事件循环中并非只维护一个队列,事实上有两个队列:

  • Macrotask queue :ajax 、setTimeout、setInterval、DOM监听、UI Rendering等
  • Mircrotask queue:Promise的then回调、queueMicrotask()、Mutation Observer API等

# 浏览器中的 JS 运行时环境

尽管 JS 已经能够运行在多种不同的环境中,但其主战场仍然是浏览器。其中一个非常主要的发明 JS 的原因,是为了能够在网页中加入动态 HTML 和 CSS 效果。

一个浏览器充当了网页交互界面的角色。用户点击某个界面元素,滚动页面,或通过键盘输入内容。所有这些行为都会触发用户事件,而这些用户事件也是浏览器开放给 JS 运行时环境的。

此外,用户并不是唯一的事件来源,JS 脚本同样能够创建事件,比如定时器、延时器。

但这仍不是全部,通过浏览器开放的网络请求 API,能够与应用的服务端进行通讯。所以,网络请求的返回结果同样也是一种事件

最后,浏览器还提供了访问宿主操作系统的基本信息,以及使用本地存储来保存数据的能力。

image-20230606181157427

# 浏览器环境之外的 JS

在相当长的一个时期内,提及 JS,只会联想到客户端应用的前端开发。而现在,这门语言已经在很多方面大展拳脚。包括:

  • 服务端开发 - NodeJS,史上最强的 JS 运行时环境。NodeJS 提供文件系统访问能力,网络请求能力以及其他在浏览器上无法实现的能力。
  • 网络传输 - JSON 格式,目前最流行也是可读性最高的的数据传输格式,正是起源于 JS。
  • 移动客户端 - JS 语言凭借其独立于任何一家移动设备厂商的先天优势,成为众多移动设备平台首选的中间语言。比如 PhoneGap (opens new window)Appcelerator Titanium (opens new window) 框架,就试图推出一个统一的开发平台,来兼容多种移动设备和操作系统。
  • 数据库 - 你会发现使用 JS 实现的数据库不计其数。当然也有其他用途,比如,作为 MongoDB (opens new window) 的查询语言就是基于 JS 语法。

实际上,你可以构建一个完完整整的 web 应用(从 UI 层面到数据访问层面),这一切,仅仅基于 JS 技术栈即可实现。而这个技术栈,还有属于自己的名字,叫做 MEAN stack (opens new window)

# JS 引擎 vs 运行时环境

读到这里,我想相信你已经可以用自己的理解来区分 JS 引擎和 JS 运行时环境了。由于这些话题实在太容易混淆了,我们最后再总结一下他们的区别。

JS 引擎负责将脚本翻译为可执行的机器码指令,以便通过宿主机器的 CPU 来执行。引擎在代码执行期间进行脚本翻译。应用不运行,脚本就不会被编译。

JS 运行时环境提供了脚本执行期间所需要的工具类库。我们编写的脚本依赖于这些类库。而引擎本身并不对工具类库产生依赖。 最赞的是,JS 引擎的实现,完全不需要依赖于运行时环境。这的确与所想象的不同,引擎的开发并非基于某个运行环境。 举个现实场景的例子来进行说明: 如 V8 引擎,既可应用于谷歌浏览器,也能在 NodeJS 中找到它的身影。同一个引擎成功应用在了两种用途截然不同的环境中。

# 总结

你需要掌握哪些? 首先,我们介绍了 JS 引擎这个基本概念。紧接着介绍了 JS 运行时环境的特性和组成部分。比如,事件循环机制,事件,事件处理队列,微任务,宏任务。最后,通过对比的方式,阐述了 JS 引擎与 JS 运行时环境的区别。

Last Updated: 6/6/2023, 5:22:35 PM