运行时和引擎经常被错误地认为是同一个东西。
您可能听说过“JavaScript 引擎”和“JavaScript 运行时”这两个术语可以互换使用,表示“运行 JavaScript 的程序”。它们经常通过引用 V8、Node.js 或其他相关程序组合来混合使用。但是,JavaScript 引擎和 JavaScript 运行时在范围和功能方面存在显著差异。理解这一差异是全面理解 JavaScript 语言的关键。
简单总结来说:JavaScript引擎实现ECMAScript标准,而JavaScript运行时环境在此基础上添加输入/输出等附加功能,并可根据需要定义API。
在展开讨论引擎与运行时之间有何区别之前,有必要先定义几个经常与引擎和运行时结合使用的术语:ECMAScript 和 JavaScript。
什么是 ECMAScript?
1996 年,Netscape 和 Sun Microsystems 与非营利性标准组织 Ecma International 接洽,商讨如何标准化 JavaScript。这项努力的成果是 ECMA-262,该规范于 1997 年发布,定义了 JavaScript 实现的工作方式。由于 Sun 不愿意捐赠 JavaScript 商标,该规范必须使用不同的名称,因此 ECMA-262 被命名为“ECMAScript 语言规范”。因此,ECMAScript 是 ECMA-262 中指定的语言的名称。
ECMAScript定义了JavaScript的核心功能,这些功能必须由符合规范的实现来提供,无论它们被嵌入到哪里(嵌入实现的程序被称为宿主)。即使在早期阶段,Netscape 也打算不仅在浏览器中使用 JavaScript,而且还在服务器上使用,而 ECMAScript 将作为这两种实现的核心。因此,ECMAScript 不包含任何与 Web 相关的功能,也不包含任何输入或输出数据的方式(此类功能必须由宿主提供)。这意味着 ECMAScript 包含标准全局类(例如Object
、Array
和Promise
),但不包含HTMLElement
、setTimeout
或fetch()
。
什么是 JavaScript?
“JavaScript”一词没有正式的定义,但人们普遍认为它是ECMAScript语言的一个超集。这意味着JavaScript除了实现ECMAScript之外,还实现了其他规范和其他功能。在其早期形式中,JavaScript被认为是ECMAScript加上基于Web的API,如文档对象模型(DOM)和基于浏览器的API,如 History API。如今,JavaScript被认为是ECMAScript加上任何其他宿主提供的API的任何组合。这包括针对浏览器的特定于Web的API和针对如Node.js等宿主的特定于服务器的API。
什么是 JavaScript 引擎?
通常所说的 JavaScript 引擎更准确地说应该是 ECMAScript 引擎,因为它们实现了 ECMA-262,没有任何(或太多)附加功能。JavaScript 引擎旨在嵌入到宿主中,而宿主又定义了用于输入和输出的附加功能。
最知名的 JavaScript 引擎是:
-
V8 - 作为 Chromium 项目的 JavaScript 引擎而创建,现在也用于 Node.js 和 Deno。由于 Edge 和 Opera 基于 Chromium,因此 V8 是最常用的 JavaScript 引擎。
-
JavaScriptCore - 作为 Safari 的 JavaScript 引擎创建,适用于 MacOS 和 iOS,也可用于 Bun。
由于 JavaScript 引擎仅实现 ECMAScript 并且可以通过宿主进行扩展,因此它们可以在各种不同的运行时环境中使用。
什么是 JavaScript 运行时?
JavaScript运行时环境是一个ECMAScript宿主,意味着它是一个嵌入JavaScript引擎的程序。Chrome、Firefox、Edge、Safari、Node.js、Deno和Bun都是JavaScript运行时环境,因为它们都嵌入了JavaScript引擎,并定义了可通过JavaScript访问的额外功能。Web浏览器实现了DOM和其他Web API,而服务器端运行时环境则实现了文件系统访问功能。
对于 JavaScript 运行时可以添加哪些附加功能,没有任何规则;运行时开发人员可以自行决定。这就是为什么 Node.js、Deno 和 Bun 都以不同的方式实现文件系统,以及为什么 Deno 决定青睐 Web API,例如fetch()
Node.js 最初决定实现自己的 HTTP 客户端(Node.js 此后也采用了fetch()
)。使JavaScript运行环境变得独一无二的,不仅仅是JavaScript的API,还有它们使用JavaScript引擎的方式。
例如,事件循环(允许运行时在运行 JavaScript 和执行其他任务之间切换的过程)未在 ECMA-262 中定义,因此未在任何 JavaScript 引擎中实现。每个 JavaScript 运行时都应实现自己的事件循环。Web 浏览器在其 HTML 规范中定义了自己的事件循环版本,但 Node.js 等服务器端运行时环境则定义了它们自己的事件循环。虽然事件循环不是JavaScript运行时环境的必需组件,但它通常出现在通用JavaScript运行时环境中。
总结
JavaScript引擎和JavaScript运行时环境是相关的,但并不完全相同。JavaScript引擎实现了ECMA-262标准中定义的ECMAScript。ECMA-262定义了JavaScript的核心功能,但不提供任何输入或输出功能。而JavaScript运行时环境是一个ECMAScript宿主,它嵌入了一个JavaScript引擎,并为其增加了输入和输出的附加功能,以及运行时所需的其他任何功能。这些附加功能可能包括Web浏览器中的DOM或服务器端运行时环境中的文件系统访问。运行时环境没有义务遵循其他标准,并且可以根据需要定义自己的API,这就是为什么Node.js、Deno和Bun都有各自不同的文件系统API的原因。