- assert 断言
- async_hooks 异步钩子
- async_hooks/context 异步上下文
- buffer 缓冲区
- C++插件
- C/C++插件(使用 Node-API)
- C++嵌入器
- child_process 子进程
- cluster 集群
- CLI 命令行
- console 控制台
- Corepack 核心包
- crypto 加密
- crypto/webcrypto 网络加密
- debugger 调试器
- deprecation 弃用
- dgram 数据报
- diagnostics_channel 诊断通道
- dns 域名服务器
- domain 域
- Error 错误
- events 事件触发器
- fs 文件系统
- global 全局变量
- http 超文本传输协议
- http2 超文本传输协议 2.0
- https 安全超文本传输协议
- inspector 检查器
- Intl 国际化
- module 模块
- module/cjs CommonJS 模块
- module/esm ECMAScript 模块
- module/package 包模块
- net 网络
- os 操作系统
- path 路径
- perf_hooks 性能钩子
- permission 权限
- process 进程
- punycode 域名代码
- querystring 查询字符串
- readline 逐行读取
- repl 交互式解释器
- report 诊断报告
- sea 单个可执行应用程序
- stream 流
- stream/web 网络流
- string_decoder 字符串解码器
- test 测试
- timers 定时器
- tls 安全传输层
- trace_events 跟踪事件
- tty 终端
- url 网址
- util 实用工具
- v8 引擎
- vm 虚拟机
- wasi 网络汇编系统接口
- worker_threads 工作线程
- zlib 压缩
Node.js v18.20.8 文档
- Node.js v18.20.8
-
目录
- 虚拟机(执行 JavaScript)
- 类:
vm.Script - 类:
vm.Module - 类:
vm.SourceTextModule - 类:
vm.SyntheticModule vm.compileFunction(code[, params[, options]])vm.createContext([contextObject[, options]])vm.isContext(object)vm.measureMemory([options])vm.runInContext(code, contextifiedObject[, options])vm.runInNewContext(code[, contextObject[, options]])vm.runInThisContext(code[, options])- 示例:在虚拟机中运行 HTTP 服务器
- 给一个对象“上下文化”是什么意思?
- 与异步任务和 Promises 的超时交互
- 类:
- 虚拟机(执行 JavaScript)
-
导航
- assert 断言
- async_hooks 异步钩子
- async_hooks/context 异步上下文
- buffer 缓冲区
- C++插件
- C/C++插件(使用 Node-API)
- C++嵌入器
- child_process 子进程
- cluster 集群
- CLI 命令行
- console 控制台
- Corepack 核心包
- crypto 加密
- crypto/webcrypto 网络加密
- debugger 调试器
- deprecation 弃用
- dgram 数据报
- diagnostics_channel 诊断通道
- dns 域名服务器
- domain 域
- Error 错误
- events 事件触发器
- fs 文件系统
- global 全局变量
- http 超文本传输协议
- http2 超文本传输协议 2.0
- https 安全超文本传输协议
- inspector 检查器
- Intl 国际化
- module 模块
- module/cjs CommonJS 模块
- module/esm ECMAScript 模块
- module/package 包模块
- net 网络
- os 操作系统
- path 路径
- perf_hooks 性能钩子
- permission 权限
- process 进程
- punycode 域名代码
- querystring 查询字符串
- readline 逐行读取
- repl 交互式解释器
- report 诊断报告
- sea 单个可执行应用程序
- stream 流
- stream/web 网络流
- string_decoder 字符串解码器
- test 测试
- timers 定时器
- tls 安全传输层
- trace_events 跟踪事件
- tty 终端
- url 网址
- util 实用工具
- v8 引擎
- vm 虚拟机
- wasi 网络汇编系统接口
- worker_threads 工作线程
- zlib 压缩
- 其他版本
虚拟机(执行 JavaScript)#>
【VM (executing JavaScript)】
源代码: lib/vm.js
node:vm 模块可以在 V8 虚拟机上下文中编译和运行代码。
【The node:vm module enables compiling and running code within V8 Virtual
Machine contexts.】
node:vm 模块不是一种安全机制。不要用它来运行不可信的代码。
JavaScript 代码可以立即编译并运行,也可以先编译、保存,然后再运行。
【JavaScript code can be compiled and run immediately or compiled, saved, and run later.】
一个常见的用例是在不同的 V8 上下文中运行代码。这意味着被调用的代码拥有与调用代码不同的全局对象。
【A common use case is to run the code in a different V8 Context. This means invoked code has a different global object than the invoking code.】
可以通过 上下文化 一个对象来提供上下文。被调用的代码会将上下文中的任何属性视为全局变量。被调用的代码对全局变量所做的任何更改都会反映在上下文对象中。
【One can provide the context by contextifying an object. The invoked code treats any property in the context like a global variable. Any changes to global variables caused by the invoked code are reflected in the context object.】
const vm = require('node:vm');
const x = 1;
const context = { x: 2 };
vm.createContext(context); // Contextify the object.
const code = 'x += 40; var y = 17;';
// `x` and `y` are global variables in the context.
// Initially, x has the value 2 because that is the value of context.x.
vm.runInContext(code, context);
console.log(context.x); // 42
console.log(context.y); // 17
console.log(x); // 1; y is not defined.
类:vm.Script#>
【Class: vm.Script】
vm.Script 类的实例包含预编译的脚本,这些脚本可以在特定上下文中执行。
【Instances of the vm.Script class contain precompiled scripts that can be
executed in specific contexts.】
new vm.Script(code[, options])#>
code<string> 要编译的 JavaScript 代码。options<Object> | <string>filename<string> 指定此脚本生成的堆栈跟踪中使用的文件名。默认值:'evalmachine.<anonymous>'。lineOffset<number> 指定此脚本生成的堆栈追踪中显示的行号偏移量。默认值:0。columnOffset<number> 指定此脚本生成的堆栈跟踪中显示的首行列号偏移量。默认值:0。cachedData<Buffer> | <TypedArray> | <DataView> 提供一个可选的Buffer、TypedArray或DataView,其中包含 V8 针对提供的源代码的代码缓存数据。当提供此数据时,cachedDataRejected的值将根据 V8 是否接受数据而被设置为true或false。produceCachedData<boolean> 当设置为true且没有现有的cachedData时,V8 将尝试为code生成代码缓存数据。成功时,将生成一个包含 V8 代码缓存数据的Buffer并存储在返回的vm.Script实例的cachedData属性中。cachedDataProduced的值将根据代码缓存数据是否生成成功而设置为true或false。此选项已 弃用,建议使用script.createCachedData()。默认值:false。importModuleDynamically<Function> 在评估此模块时被调用,当调用import()时触发。如果未指定此选项,对import()的调用将会被ERR_VM_DYNAMIC_IMPORT_CALLBACK_MISSING拒绝。此选项是实验性模块 API 的一部分。我们不建议在生产环境中使用它。如果未设置--experimental-vm-modules,此回调将被忽略,并且对import()的调用将被ERR_VM_DYNAMIC_IMPORT_CALLBACK_MISSING_FLAG拒绝。specifier<string> 传递给import()的指定符script<vm.Script>importAttributes<Object> 传递给optionsExpression可选参数的"with"值,如果未提供值,则为一个空对象。- 返回:<vm.Module> 建议返回
vm.Module,以便利用错误跟踪,并避免命名空间中包含then函数导出时出现问题。
如果 options 是字符串,那么它指定文件名。
【If options is a string, then it specifies the filename.】
创建一个新的 vm.Script 对象会编译 code,但不会执行它。编译后的 vm.Script 可以在之后多次运行。code 不会绑定到任何全局对象;相反,它会在每次运行之前绑定,仅用于那次运行。
【Creating a new vm.Script object compiles code but does not run it. The
compiled vm.Script can be run later multiple times. The code is not bound to
any global object; rather, it is bound before each run, just for that run.】
script.cachedDataRejected#>
当向 vm.Script 提供 cachedData 时,该值将根据 V8 是否接受数据被设置为 true 或 false。否则,该值为 undefined。
【When cachedData is supplied to create the vm.Script, this value will be set
to either true or false depending on acceptance of the data by V8.
Otherwise the value is undefined.】
script.createCachedData()#>
- 返回: <Buffer>
创建一个代码缓存,可用于 Script 构造函数的 cachedData 选项。返回一个 Buffer。此方法可以在任何时间、调用任意次数。
【Creates a code cache that can be used with the Script constructor's
cachedData option. Returns a Buffer. This method may be called at any
time and any number of times.】
Script 的代码缓存不包含任何可被 JavaScript 观察的状态。代码缓存可以安全地与脚本源一起保存,并可多次用来构建新的 Script 实例。
【The code cache of the Script doesn't contain any JavaScript observable
states. The code cache is safe to be saved along side the script source and
used to construct new Script instances multiple times.】
Script 源中的函数可以被标记为延迟编译,它们在构建 Script 时不会被编译。这些函数会在第一次被调用时进行编译。代码缓存会序列化 V8 当前已知的关于 Script 的元数据,这些元数据可以用来加速未来的编译。
【Functions in the Script source can be marked as lazily compiled and they are
not compiled at construction of the Script. These functions are going to be
compiled when they are invoked the first time. The code cache serializes the
metadata that V8 currently knows about the Script that it can use to speed up
future compilations.】
const script = new vm.Script(`
function add(a, b) {
return a + b;
}
const x = add(1, 2);
`);
const cacheWithoutAdd = script.createCachedData();
// In `cacheWithoutAdd` the function `add()` is marked for full compilation
// upon invocation.
script.runInThisContext();
const cacheWithAdd = script.createCachedData();
// `cacheWithAdd` contains fully compiled function `add()`.
script.runInContext(contextifiedObject[, options])#>
contextifiedObject<Object> 由vm.createContext()方法返回的 情境化 对象。options<Object>- 返回值: <any> 脚本中最后一条执行语句的结果。
在给定的 contextifiedObject 中运行 vm.Script 对象包含的已编译代码并返回结果。运行的代码无法访问局部作用域。
【Runs the compiled code contained by the vm.Script object within the given
contextifiedObject and returns the result. Running code does not have access
to local scope.】
以下示例编译了一个代码,该代码会增加一个全局变量的值,设置另一个全局变量的值,然后多次执行该代码。全局变量包含在 context 对象中。
【The following example compiles code that increments a global variable, sets
the value of another global variable, then execute the code multiple times.
The globals are contained in the context object.】
const vm = require('node:vm');
const context = {
animal: 'cat',
count: 2,
};
const script = new vm.Script('count += 1; name = "kitty";');
vm.createContext(context);
for (let i = 0; i < 10; ++i) {
script.runInContext(context);
}
console.log(context);
// Prints: { animal: 'cat', count: 12, name: 'kitty' }
使用 timeout 或 breakOnSigint 选项会导致启动新的事件循环和相应的线程,这会带来一定的性能开销。
【Using the timeout or breakOnSigint options will result in new event loops
and corresponding threads being started, which have a non-zero performance
overhead.】
script.runInNewContext([contextObject[, options]])#>
contextObject<Object> 一个将被 情境化 的对象。如果undefined,将会创建一个新对象。options<Object>displayErrors<boolean> 当设置为true时,如果在编译code时发生Error错误,导致错误的代码行会附加到堆栈跟踪中。默认值:true。timeout<integer> 指定执行code前的毫秒数,然后终止执行。如果执行被终止,将抛出Error。该值必须是严格正整数。breakOnSigint<boolean> 如果为true,接收到SIGINT(Ctrl+C)将终止执行并抛出一个Error。通过process.on('SIGINT')添加的现有事件处理程序在脚本执行期间被禁用,但之后继续有效。默认值:false。contextName<string> 新创建上下文的人类可读名称。 默认值:'VM Context i',其中i是已创建上下文的递增数字索引。contextOrigin<string> 起源 对应于新创建的用于显示的上下文。其来源应格式化为类似 URL 的形式,但只包含协议、主机和端口(如有必要),例如URL对象的url.origin属性的值。最重要的是,该字符串应省略末尾的斜杠,因为那表示路径。 默认值:''。contextCodeGeneration<Object>microtaskMode<string> 如果设置为afterEvaluate,微任务(通过Promise和async function调度的任务)将在脚本运行后立即执行。在这种情况下,它们会包含在timeout和breakOnSigint范围内。
- 返回:<any> 脚本中最后执行的语句的结果。
首先将给定的 contextObject 置于上下文中,然后在创建的上下文中运行 vm.Script 对象包含的已编译代码,并返回结果。运行的代码无法访问本地作用域。
【First contextifies the given contextObject, runs the compiled code contained
by the vm.Script object within the created context, and returns the result.
Running code does not have access to local scope.】
以下示例编译代码,该代码设置一个全局变量,然后在不同的上下文中多次执行该代码。全局变量在每个独立的 context 中设置并包含。
【The following example compiles code that sets a global variable, then executes
the code multiple times in different contexts. The globals are set on and
contained within each individual context.】
const vm = require('node:vm');
const script = new vm.Script('globalVar = "set"');
const contexts = [{}, {}, {}];
contexts.forEach((context) => {
script.runInNewContext(context);
});
console.log(contexts);
// Prints: [{ globalVar: 'set' }, { globalVar: 'set' }, { globalVar: 'set' }]
script.runInThisContext([options])#>
在当前 global 对象的上下文中运行 vm.Script 所包含的已编译代码。运行的代码无法访问局部作用域,但 可以 访问当前的 global 对象。
【Runs the compiled code contained by the vm.Script within the context of the
current global object. Running code does not have access to local scope, but
does have access to the current global object.】
以下示例编译了一个递增 global 变量的代码,然后多次执行该代码:
【The following example compiles code that increments a global variable then
executes that code multiple times:】
const vm = require('node:vm');
global.globalVar = 0;
const script = new vm.Script('globalVar += 1', { filename: 'myfile.vm' });
for (let i = 0; i < 1000; ++i) {
script.runInThisContext();
}
console.log(globalVar);
// 1000
script.sourceMapURL#>
当脚本从包含源映射魔法注释的源代码编译时,该属性将被设置为源映射的 URL。
【When the script is compiled from a source that contains a source map magic comment, this property will be set to the URL of the source map.】
import vm from 'node:vm';
const script = new vm.Script(`
function myFunc() {}
//# sourceMappingURL=sourcemap.json
`);
console.log(script.sourceMapURL);
// Prints: sourcemap.jsonconst vm = require('node:vm');
const script = new vm.Script(`
function myFunc() {}
//# sourceMappingURL=sourcemap.json
`);
console.log(script.sourceMapURL);
// Prints: sourcemap.json
类:vm.Module#>
【Class: vm.Module】
此功能仅在启用 --experimental-vm-modules 命令标志时可用。
【This feature is only available with the --experimental-vm-modules command
flag enabled.】
vm.Module 类提供了在 VM 上下文中使用 ECMAScript 模块的底层接口。它是 vm.Script 类的对应物,紧密对应于 ECMAScript 规范中定义的 模块记录s。
【The vm.Module class provides a low-level interface for using
ECMAScript modules in VM contexts. It is the counterpart of the vm.Script
class that closely mirrors Module Records as defined in the ECMAScript
specification.】
然而,与 vm.Script 不同,每个 vm.Module 对象从创建起就绑定到一个上下文。vm.Module 对象的操作本质上是异步的,这与 vm.Script 对象的同步性质形成对比。使用 'async' 函数可以帮助操作 vm.Module 对象。
【Unlike vm.Script however, every vm.Module object is bound to a context from
its creation. Operations on vm.Module objects are intrinsically asynchronous,
in contrast with the synchronous nature of vm.Script objects. The use of
'async' functions can help with manipulating vm.Module objects.】
使用 vm.Module 对象需要三个不同的步骤:创建/解析、链接和评估。以下示例说明了这三个步骤。
【Using a vm.Module object requires three distinct steps: creation/parsing,
linking, and evaluation. These three steps are illustrated in the following
example.】
这个实现比 ECMAScript 模块加载器 处于更底层。目前也无法与 Loader 交互,但计划未来支持。
【This implementation lies at a lower level than the ECMAScript Module loader. There is also no way to interact with the Loader yet, though support is planned.】
import vm from 'node:vm';
const contextifiedObject = vm.createContext({
secret: 42,
print: console.log,
});
// Step 1
//
// Create a Module by constructing a new `vm.SourceTextModule` object. This
// parses the provided source text, throwing a `SyntaxError` if anything goes
// wrong. By default, a Module is created in the top context. But here, we
// specify `contextifiedObject` as the context this Module belongs to.
//
// Here, we attempt to obtain the default export from the module "foo", and
// put it into local binding "secret".
const bar = new vm.SourceTextModule(`
import s from 'foo';
s;
print(s);
`, { context: contextifiedObject });
// Step 2
//
// "Link" the imported dependencies of this Module to it.
//
// The provided linking callback (the "linker") accepts two arguments: the
// parent module (`bar` in this case) and the string that is the specifier of
// the imported module. The callback is expected to return a Module that
// corresponds to the provided specifier, with certain requirements documented
// in `module.link()`.
//
// If linking has not started for the returned Module, the same linker
// callback will be called on the returned Module.
//
// Even top-level Modules without dependencies must be explicitly linked. The
// callback provided would never be called, however.
//
// The link() method returns a Promise that will be resolved when all the
// Promises returned by the linker resolve.
//
// Note: This is a contrived example in that the linker function creates a new
// "foo" module every time it is called. In a full-fledged module system, a
// cache would probably be used to avoid duplicated modules.
async function linker(specifier, referencingModule) {
if (specifier === 'foo') {
return new vm.SourceTextModule(`
// The "secret" variable refers to the global variable we added to
// "contextifiedObject" when creating the context.
export default secret;
`, { context: referencingModule.context });
// Using `contextifiedObject` instead of `referencingModule.context`
// here would work as well.
}
throw new Error(`Unable to resolve dependency: ${specifier}`);
}
await bar.link(linker);
// Step 3
//
// Evaluate the Module. The evaluate() method returns a promise which will
// resolve after the module has finished evaluating.
// Prints 42.
await bar.evaluate();const vm = require('node:vm');
const contextifiedObject = vm.createContext({
secret: 42,
print: console.log,
});
(async () => {
// Step 1
//
// Create a Module by constructing a new `vm.SourceTextModule` object. This
// parses the provided source text, throwing a `SyntaxError` if anything goes
// wrong. By default, a Module is created in the top context. But here, we
// specify `contextifiedObject` as the context this Module belongs to.
//
// Here, we attempt to obtain the default export from the module "foo", and
// put it into local binding "secret".
const bar = new vm.SourceTextModule(`
import s from 'foo';
s;
print(s);
`, { context: contextifiedObject });
// Step 2
//
// "Link" the imported dependencies of this Module to it.
//
// The provided linking callback (the "linker") accepts two arguments: the
// parent module (`bar` in this case) and the string that is the specifier of
// the imported module. The callback is expected to return a Module that
// corresponds to the provided specifier, with certain requirements documented
// in `module.link()`.
//
// If linking has not started for the returned Module, the same linker
// callback will be called on the returned Module.
//
// Even top-level Modules without dependencies must be explicitly linked. The
// callback provided would never be called, however.
//
// The link() method returns a Promise that will be resolved when all the
// Promises returned by the linker resolve.
//
// Note: This is a contrived example in that the linker function creates a new
// "foo" module every time it is called. In a full-fledged module system, a
// cache would probably be used to avoid duplicated modules.
async function linker(specifier, referencingModule) {
if (specifier === 'foo') {
return new vm.SourceTextModule(`
// The "secret" variable refers to the global variable we added to
// "contextifiedObject" when creating the context.
export default secret;
`, { context: referencingModule.context });
// Using `contextifiedObject` instead of `referencingModule.context`
// here would work as well.
}
throw new Error(`Unable to resolve dependency: ${specifier}`);
}
await bar.link(linker);
// Step 3
//
// Evaluate the Module. The evaluate() method returns a promise which will
// resolve after the module has finished evaluating.
// Prints 42.
await bar.evaluate();
})();
module.dependencySpecifiers#>
- 字符串[]
该模块所有依赖的说明符。返回的数组是冻结的,不能对其进行任何更改。
【The specifiers of all dependencies of this module. The returned array is frozen to disallow any changes to it.】
对应于 ECMAScript 规范中 循环模块记录 的 [[RequestedModules]] 字段。
【Corresponds to the [[RequestedModules]] field of Cyclic Module Records in
the ECMAScript specification.】
module.error#>
如果 module.status 为 'errored',则该属性包含模块在评估期间抛出的异常。如果状态是其他值,访问此属性将导致抛出异常。
【If the module.status is 'errored', this property contains the exception
thrown by the module during evaluation. If the status is anything else,
accessing this property will result in a thrown exception.】
值 undefined 不能用于没有抛出异常的情况,因为可能会与 throw undefined; 混淆。
【The value undefined cannot be used for cases where there is not a thrown
exception due to possible ambiguity with throw undefined;.】
对应于 ECMAScript 规范中 循环模块记录 的 [[EvaluationError]] 字段。
【Corresponds to the [[EvaluationError]] field of Cyclic Module Records
in the ECMAScript specification.】
module.evaluate([options])#>
评估模块。
【Evaluate the module.】
必须在模块已链接后调用此方法;否则将会被拒绝。它也可以在模块已经被评估之后调用,在这种情况下,如果初始评估成功结束(module.status 为 'evaluated'),它将什么也不做;如果初始评估导致了异常(module.status 为 'errored'),它将重新抛出该异常。
【This must be called after the module has been linked; otherwise it will reject.
It could be called also when the module has already been evaluated, in which
case it will either do nothing if the initial evaluation ended in success
(module.status is 'evaluated') or it will re-throw the exception that the
initial evaluation resulted in (module.status is 'errored').】
在模块正在被评估时(module.status 为 'evaluating'),无法调用此方法。
【This method cannot be called while the module is being evaluated
(module.status is 'evaluating').】
对应 ECMAScript 规范中 循环模块记录 的 Evaluate() 具体方法 字段。
【Corresponds to the Evaluate() concrete method field of Cyclic Module Records in the ECMAScript specification.】
module.identifier#>
当前模块的标识符,在构造函数中设置。
【The identifier of the current module, as set in the constructor.】
module.link(linker)#>
linker<Function>-
specifier<string> 所请求模块的说明符:import foo from 'foo'; // ^^^^^ the module specifier -
referencingModule<vm.Module> 调用link()的Module对象。 -
extra<Object> -
返回: <vm.Module> | <Promise>
-
- 返回: <Promise>
链接模块依赖。这种方法必须在评估之前调用,并且每个模块只能调用一次。
【Link module dependencies. This method must be called before evaluation, and can only be called once per module.】
该函数预计返回一个 Module 对象或一个最终解析为 Module 对象的 Promise。返回的 Module 必须满足以下两个不变量:
【The function is expected to return a Module object or a Promise that
eventually resolves to a Module object. The returned Module must satisfy the
following two invariants:】
- 它必须属于与父
Module相同的上下文。 - 它的
status不能为'errored'。
如果返回的 Module 的 status 是 'unlinked',此方法将会对返回的 Module 递归调用,并使用相同提供的 linker 函数。
【If the returned Module's status is 'unlinked', this method will be
recursively called on the returned Module with the same provided linker
function.】
link() 返回一个 Promise,当所有链接实例解析为有效的 Module 时,该 Promise 会被解决;如果链接器函数抛出异常或返回无效的 Module,则 Promise 会被拒绝。
链接器函数大致对应于 ECMAScript 规范中实现定义的 主机解析导入模块 抽象操作,但有一些关键差异:
【The linker function roughly corresponds to the implementation-defined HostResolveImportedModule abstract operation in the ECMAScript specification, with a few key differences:】
- 链接器函数可以是异步的,而 主机解析导入模块 是同步的。
在模块链接期间使用的实际 主机解析导入模块 实现会返回在链接过程中被链接的模块。由于在那时所有模块都已经完全链接,因此 主机解析导入模块 实现根据规范是完全同步的。
【The actual HostResolveImportedModule implementation used during module linking is one that returns the modules linked during linking. Since at that point all modules would have been fully linked already, the HostResolveImportedModule implementation is fully synchronous per specification.】
对应 ECMAScript 规范中 循环模块记录 的 Link() 具体方法 字段。
【Corresponds to the Link() concrete method field of Cyclic Module Records in the ECMAScript specification.】
module.namespace#>
模块的命名空间对象。只有在链接完成(module.link())之后才能使用。
【The namespace object of the module. This is only available after linking
(module.link()) has completed.】
对应于 ECMAScript 规范中的 获取模块命名空间 抽象操作。
【Corresponds to the GetModuleNamespace abstract operation in the ECMAScript specification.】
module.status#>
模块的当前状态。可能为以下之一:
【The current status of the module. Will be one of:】
-
'unlinked':module.link()尚未被调用。 -
'linking':module.link()已被调用,但链接器函数返回的所有 Promise 尚未全部解决。 -
'linked':模块已成功链接,并且它的所有依赖也已链接,但尚未调用module.evaluate()。 -
'evaluating':该模块正在通过对自身或父模块调用module.evaluate()进行评估。 -
'evaluated':模块已成功评估。 -
'errored':模块已被评估,但抛出了异常。
除了 'errored',这个状态字符串对应规范中 循环模块记录 的 [[Status]] 字段。'errored' 对应规范中的 'evaluated',但 [[EvaluationError]] 被设置为一个不为 undefined 的值。
【Other than 'errored', this status string corresponds to the specification's
Cyclic Module Record's [[Status]] field. 'errored' corresponds to
'evaluated' in the specification, but with [[EvaluationError]] set to a
value that is not undefined.】
类:vm.SourceTextModule#>
【Class: vm.SourceTextModule】
此功能仅在启用 --experimental-vm-modules 命令标志时可用。
【This feature is only available with the --experimental-vm-modules command
flag enabled.】
- 继承: <vm.Module>
vm.SourceTextModule 类提供了 ECMAScript 规范中定义的 源文本模块记录。
【The vm.SourceTextModule class provides the Source Text Module Record as
defined in the ECMAScript specification.】
new vm.SourceTextModule(code[, options])#>
code<string> JavaScript 模块解析代码optionsidentifier<string> 用于堆栈跟踪的字符串。 默认值:'vm:module(i)',其中i是上下文相关的递增索引。cachedData<Buffer> | <TypedArray> | <DataView> 提供一个可选的Buffer、TypedArray或DataView,包含 V8 为所提供源代码生成的代码缓存数据。code必须与创建此cachedData的模块相同。context<Object> 情境化 对象,由vm.createContext()方法返回,用于在其中编译和评估此Module。如果未指定上下文,则模块将在当前执行上下文中评估。lineOffset<integer> 指定在此Module生成的堆栈跟踪中显示的行号偏移量。默认值:0。columnOffset<integer> 指定此Module生成的堆栈跟踪中显示的首行列号偏移。默认值:0。initializeImportMeta<Function> 在评估此Module时被调用,用于初始化import.meta。meta<import.meta>module<vm.SourceTextModule>
importModuleDynamically<Function> 在评估此模块时调用,当调用import()时触发。如果未指定此选项,对import()的调用将被ERR_VM_DYNAMIC_IMPORT_CALLBACK_MISSING拒绝。如果未设置--experimental-vm-modules,此回调将被忽略,对import()的调用将被ERR_VM_DYNAMIC_IMPORT_CALLBACK_MISSING_FLAG拒绝。specifier<string> 传递给import()的指定符module<vm.Module>- 'importAttributes' <Object> 'assert' 值传递给
optionsExpression可选参数,若无值则为空对象 被提供了。 - 返回:<vm.Module> 建议返回
vm.Module,以便利用错误跟踪,并避免命名空间中包含then函数导出时出现问题。
创建一个新的 SourceTextModule 实例。
【Creates a new SourceTextModule instance.】
分配给 import.meta 对象的属性如果是对象,可能允许模块访问指定 context 之外的信息。使用 vm.runInContext() 可以在特定上下文中创建对象。
【Properties assigned to the import.meta object that are objects may
allow the module to access information outside the specified context. Use
vm.runInContext() to create objects in a specific context.】
import vm from 'node:vm';
const contextifiedObject = vm.createContext({ secret: 42 });
const module = new vm.SourceTextModule(
'Object.getPrototypeOf(import.meta.prop).secret = secret;',
{
initializeImportMeta(meta) {
// Note: this object is created in the top context. As such,
// Object.getPrototypeOf(import.meta.prop) points to the
// Object.prototype in the top context rather than that in
// the contextified object.
meta.prop = {};
},
});
// Since module has no dependencies, the linker function will never be called.
await module.link(() => {});
await module.evaluate();
// Now, Object.prototype.secret will be equal to 42.
//
// To fix this problem, replace
// meta.prop = {};
// above with
// meta.prop = vm.runInContext('{}', contextifiedObject);const vm = require('node:vm');
const contextifiedObject = vm.createContext({ secret: 42 });
(async () => {
const module = new vm.SourceTextModule(
'Object.getPrototypeOf(import.meta.prop).secret = secret;',
{
initializeImportMeta(meta) {
// Note: this object is created in the top context. As such,
// Object.getPrototypeOf(import.meta.prop) points to the
// Object.prototype in the top context rather than that in
// the contextified object.
meta.prop = {};
},
});
// Since module has no dependencies, the linker function will never be called.
await module.link(() => {});
await module.evaluate();
// Now, Object.prototype.secret will be equal to 42.
//
// To fix this problem, replace
// meta.prop = {};
// above with
// meta.prop = vm.runInContext('{}', contextifiedObject);
})();
sourceTextModule.createCachedData()#>
- 返回: <Buffer>
创建一个可与 SourceTextModule 构造函数的 cachedData 选项一起使用的代码缓存。返回一个 Buffer。在模块被执行之前,此方法可以调用任意次。
【Creates a code cache that can be used with the SourceTextModule constructor's
cachedData option. Returns a Buffer. This method may be called any number
of times before the module has been evaluated.】
SourceTextModule 的代码缓存不包含任何 JavaScript 可观察状态。代码缓存可以安全地与脚本源一起保存,并可用于多次构建新的 SourceTextModule 实例。
【The code cache of the SourceTextModule doesn't contain any JavaScript
observable states. The code cache is safe to be saved along side the script
source and used to construct new SourceTextModule instances multiple times.】
SourceTextModule 源中的函数可以标记为延迟编译,它们在 SourceTextModule 构建时不会被编译。这些函数将在第一次被调用时编译。代码缓存会序列化 V8 当前已知的关于 SourceTextModule 的元数据,这些元数据可以用来加速未来的编译。
【Functions in the SourceTextModule source can be marked as lazily compiled
and they are not compiled at construction of the SourceTextModule. These
functions are going to be compiled when they are invoked the first time. The
code cache serializes the metadata that V8 currently knows about the
SourceTextModule that it can use to speed up future compilations.】
// Create an initial module
const module = new vm.SourceTextModule('const a = 1;');
// Create cached data from this module
const cachedData = module.createCachedData();
// Create a new module using the cached data. The code must be the same.
const module2 = new vm.SourceTextModule('const a = 1;', { cachedData });
类:vm.SyntheticModule#>
【Class: vm.SyntheticModule】
此功能仅在启用 --experimental-vm-modules 命令标志时可用。
【This feature is only available with the --experimental-vm-modules command
flag enabled.】
- 继承: <vm.Module>
vm.SyntheticModule 类提供了 WebIDL 规范中定义的 合成模块记录。合成模块的目的是为将非 JavaScript 源暴露给 ECMAScript 模块图提供通用接口。
【The vm.SyntheticModule class provides the Synthetic Module Record as
defined in the WebIDL specification. The purpose of synthetic modules is to
provide a generic interface for exposing non-JavaScript sources to ECMAScript
module graphs.】
const vm = require('node:vm');
const source = '{ "a": 1 }';
const module = new vm.SyntheticModule(['default'], function() {
const obj = JSON.parse(source);
this.setExport('default', obj);
});
// Use `module` in linking...
new vm.SyntheticModule(exportNames, evaluateCallback[, options])#>
exportNames<string[]> 将从模块中导出的名称数组。evaluateCallback<Function> 当模块被评估时调用。options
创建一个新的 SyntheticModule 实例。
【Creates a new SyntheticModule instance.】
分配给此实例导出的对象可能允许模块的导入者访问指定 context 之外的信息。使用 vm.runInContext() 在特定上下文中创建对象。
【Objects assigned to the exports of this instance may allow importers of
the module to access information outside the specified context. Use
vm.runInContext() to create objects in a specific context.】
syntheticModule.setExport(name, value)#>
此方法在模块链接后用于设置导出值。如果在模块链接前调用,将会抛出 ERR_VM_MODULE_STATUS 错误。
【This method is used after the module is linked to set the values of exports. If
it is called before the module is linked, an ERR_VM_MODULE_STATUS error
will be thrown.】
import vm from 'node:vm';
const m = new vm.SyntheticModule(['x'], () => {
m.setExport('x', 1);
});
await m.link(() => {});
await m.evaluate();
assert.strictEqual(m.namespace.x, 1);const vm = require('node:vm');
(async () => {
const m = new vm.SyntheticModule(['x'], () => {
m.setExport('x', 1);
});
await m.link(() => {});
await m.evaluate();
assert.strictEqual(m.namespace.x, 1);
})();
vm.compileFunction(code[, params[, options]])#>
code<string> 要编译的函数体。params<string[]> 一个包含函数所有参数的字符串数组。options<Object>filename<string> 指定此脚本生成的堆栈跟踪中使用的文件名。默认值:''。lineOffset<number> 指定此脚本生成的堆栈追踪中显示的行号偏移量。默认值:0。columnOffset<number> 指定此脚本生成的堆栈跟踪中显示的首行列号偏移量。默认值:0。cachedData<Buffer> | <TypedArray> | <DataView> 提供一个可选的Buffer、TypedArray或DataView,其中包含所提供源代码的 V8 代码缓存数据。此数据必须由之前使用相同code和params调用vm.compileFunction()生成。produceCachedData<boolean> 指定是否生成新的缓存数据。 默认值:false。parsingContext<Object> 应在其中编译所述函数的 情境化 对象。contextExtensions<Object[]> 一个包含上下文扩展(封装当前作用域的对象)集合的数组,在编译时应用。默认值:[]。importModuleDynamically<Function> 在评估此模块时调用,当调用import()时触发。如果未指定此选项,调用import()将被ERR_VM_DYNAMIC_IMPORT_CALLBACK_MISSING拒绝。此选项是实验性模块 API 的一部分,不应认为是稳定的。如果未设置--experimental-vm-modules,此回调将被忽略,调用import()将被ERR_VM_DYNAMIC_IMPORT_CALLBACK_MISSING_FLAG拒绝。specifier<string> 传递给import()的指定符函数<Function>importAttributes<Object> 传递给optionsExpression可选参数的"with"值,如果未提供值,则为一个空对象。- 返回:<vm.Module> 建议返回
vm.Module,以便利用错误跟踪,并避免命名空间中包含then函数导出时出现问题。
- 回归次数:<Function>
将给定的代码编译到提供的上下文中(如果未提供上下文,则使用当前上下文),并将其封装在包含指定 params 的函数中返回。
【Compiles the given code into the provided context (if no context is
supplied, the current context is used), and returns it wrapped inside a
function with the given params.】
vm.createContext([contextObject[, options]])#>
contextObject<Object>options<Object>name<string> 新创建上下文的人类可读名称。 默认值:'VM Context i',其中i是创建的上下文的递增数字索引。origin<string> 起源 对应于新创建的上下文,用于显示目的。origin 应该像 URL 一样格式化,但只包含方案、主机和端口(如果需要),类似于URL对象的url.origin属性的值。最重要的是,这个字符串应省略尾部的斜杠,因为斜杠表示路径。默认值:''。codeGeneration<Object>microtaskMode<string> 如果设置为afterEvaluate,微任务(通过Promise和async function调度的任务)将在脚本通过script.runInContext()执行后立即运行。在这种情况下,它们包含在timeout和breakOnSigint范围内。
- 返回:<Object> 已上下文化的对象。
如果提供了 contextObject,vm.createContext() 方法将 准备那个对象,以便可以在调用 vm.runInContext() 或 script.runInContext() 时使用。在这样的脚本内部,contextObject 将成为全局对象,保留其所有现有属性,同时还拥有任何标准 全局对象 所具备的内置对象和函数。在 vm 模块运行的脚本之外,全局变量将保持不变。
【If given a contextObject, the vm.createContext() method will prepare
that object so that it can be used in calls to
vm.runInContext() or script.runInContext(). Inside such scripts,
the contextObject will be the global object, retaining all of its existing
properties but also having the built-in objects and functions any standard
global object has. Outside of scripts run by the vm module, global variables
will remain unchanged.】
const vm = require('node:vm');
global.globalVar = 3;
const context = { globalVar: 1 };
vm.createContext(context);
vm.runInContext('globalVar *= 2;', context);
console.log(context);
// Prints: { globalVar: 2 }
console.log(global.globalVar);
// Prints: 3
如果省略 contextObject(或显式传入 undefined),将返回一个新的、空的 情境化 对象。
【If contextObject is omitted (or passed explicitly as undefined), a new,
empty contextified object will be returned.】
vm.createContext() 方法主要用于创建一个可以用于运行多个脚本的单一上下文。例如,在模拟网页浏览器时,该方法可以用于创建一个表示窗口全局对象的单一上下文,然后在该上下文中一起运行所有 <script> 标签。
【The vm.createContext() method is primarily useful for creating a single
context that can be used to run multiple scripts. For instance, if emulating a
web browser, the method can be used to create a single context representing a
window's global object, then run all <script> tags together within that
context.】
通过检查器 API,可以看到所提供的上下文的 name 和 origin。
【The provided name and origin of the context are made visible through the
Inspector API.】
vm.isContext(object)#>
如果给定的 object 对象已经使用 vm.createContext() 进行了 情境化,则返回 true。
【Returns true if the given object object has been contextified using
vm.createContext().】
vm.measureMemory([options])#>
测量 V8 所知的内存,以及当前 V8 isolate 或主上下文中所有已知上下文使用的内存。
【Measure the memory known to V8 and used by all contexts known to the current V8 isolate, or the main context.】
options<Object> 可选。- 返回: <Promise> 如果内存测量成功,Promise 将解析为一个包含内存使用信息的对象。否则,将拒绝并返回
ERR_CONTEXT_NOT_INITIALIZED错误。
返回的 Promise 可能解析的对象格式是 V8 引擎特有的,并且可能会随着 V8 的不同版本而变化。
【The format of the object that the returned Promise may resolve with is specific to the V8 engine and may change from one version of V8 to the next.】
返回的结果与 v8.getHeapSpaceStatistics() 返回的统计数据不同,因为 vm.measureMemory() 测量的是当前 V8 引擎实例中每个特定 V8 上下文可访问的内存,而 v8.getHeapSpaceStatistics() 的结果测量的是当前 V8 实例中每个堆空间所占用的内存。
【The returned result is different from the statistics returned by
v8.getHeapSpaceStatistics() in that vm.measureMemory() measure the
memory reachable by each V8 specific contexts in the current instance of
the V8 engine, while the result of v8.getHeapSpaceStatistics() measure
the memory occupied by each heap space in the current V8 instance.】
const vm = require('node:vm');
// Measure the memory used by the main context.
vm.measureMemory({ mode: 'summary' })
// This is the same as vm.measureMemory()
.then((result) => {
// The current format is:
// {
// total: {
// jsMemoryEstimate: 2418479, jsMemoryRange: [ 2418479, 2745799 ]
// }
// }
console.log(result);
});
const context = vm.createContext({ a: 1 });
vm.measureMemory({ mode: 'detailed', execution: 'eager' })
.then((result) => {
// Reference the context here so that it won't be GC'ed
// until the measurement is complete.
console.log(context.a);
// {
// total: {
// jsMemoryEstimate: 2574732,
// jsMemoryRange: [ 2574732, 2904372 ]
// },
// current: {
// jsMemoryEstimate: 2438996,
// jsMemoryRange: [ 2438996, 2768636 ]
// },
// other: [
// {
// jsMemoryEstimate: 135736,
// jsMemoryRange: [ 135736, 465376 ]
// }
// ]
// }
console.log(result);
});
vm.runInContext(code, contextifiedObject[, options])#>
code<string> 用于编译和运行的 JavaScript 代码。contextifiedObject<Object> 该 情境化 对象将在code编译和运行时作为global使用。options<Object> | <string>filename<string> 指定此脚本生成的堆栈跟踪中使用的文件名。默认值:'evalmachine.<anonymous>'。lineOffset<number> 指定此脚本生成的堆栈追踪中显示的行号偏移量。默认值:0。columnOffset<number> 指定此脚本生成的堆栈跟踪中显示的首行列号偏移量。默认值:0。displayErrors<boolean> 当设置为true时,如果在编译code时发生Error错误,导致错误的代码行会附加到堆栈跟踪中。默认值:true。timeout<integer> 指定执行code前的毫秒数,然后终止执行。如果执行被终止,将抛出Error。该值必须是严格正整数。breakOnSigint<boolean> 如果为true,接收到SIGINT(Ctrl+C)将终止执行并抛出一个Error。通过process.on('SIGINT')添加的现有事件处理程序在脚本执行期间被禁用,但之后继续有效。默认值:false。cachedData<Buffer> | <TypedArray> | <DataView> 提供一个可选的Buffer、TypedArray或DataView,其中包含 V8 的代码缓存数据,用于提供的源代码。importModuleDynamically<Function> 在评估此模块时调用,当调用import()时触发。如果未指定此选项,对import()的调用将会被ERR_VM_DYNAMIC_IMPORT_CALLBACK_MISSING拒绝。此选项是实验性模块 API 的一部分。我们不建议在生产环境中使用它。如果未设置--experimental-vm-modules,此回调将被忽略,并且对import()的调用将被ERR_VM_DYNAMIC_IMPORT_CALLBACK_MISSING_FLAG拒绝。specifier<string> 传递给import()的指定符script<vm.Script>importAttributes<Object> 传递给optionsExpression可选参数的"with"值,如果未提供值,则为一个空对象。- 返回:<vm.Module> 建议返回
vm.Module,以便利用错误跟踪,并避免命名空间中包含then函数导出时出现问题。
- 返回:<any> 脚本中最后执行的语句的结果。
vm.runInContext() 方法会编译 code,在 contextifiedObject 的上下文中运行它,然后返回结果。运行的代码无法访问本地作用域。contextifiedObject 对象必须之前使用 vm.createContext() 方法被 情境化。
【The vm.runInContext() method compiles code, runs it within the context of
the contextifiedObject, then returns the result. Running code does not have
access to the local scope. The contextifiedObject object must have been
previously contextified using the vm.createContext() method.】
如果 options 是字符串,那么它指定文件名。
【If options is a string, then it specifies the filename.】
以下示例使用单个 情境化 对象编译并执行不同的脚本:
【The following example compiles and executes different scripts using a single contextified object:】
const vm = require('node:vm');
const contextObject = { globalVar: 1 };
vm.createContext(contextObject);
for (let i = 0; i < 10; ++i) {
vm.runInContext('globalVar *= 2;', contextObject);
}
console.log(contextObject);
// Prints: { globalVar: 1024 }
vm.runInNewContext(code[, contextObject[, options]])#>
code<string> 用于编译和运行的 JavaScript 代码。contextObject<Object> 一个将被 情境化 的对象。如果为undefined,将会创建一个新对象。options<Object> | <string>filename<string> 指定此脚本生成的堆栈跟踪中使用的文件名。默认值:'evalmachine.<anonymous>'。lineOffset<number> 指定此脚本生成的堆栈追踪中显示的行号偏移量。默认值:0。columnOffset<number> 指定此脚本生成的堆栈跟踪中显示的首行列号偏移量。默认值:0。displayErrors<boolean> 当设置为true时,如果在编译code时发生Error错误,导致错误的代码行会附加到堆栈跟踪中。默认值:true。timeout<integer> 指定执行code前的毫秒数,然后终止执行。如果执行被终止,将抛出Error。该值必须是严格正整数。breakOnSigint<boolean> 如果为true,接收到SIGINT(Ctrl+C)将终止执行并抛出一个Error。通过process.on('SIGINT')添加的现有事件处理程序在脚本执行期间被禁用,但之后继续有效。默认值:false。contextName<string> 新创建上下文的人类可读名称。 默认值:'VM Context i',其中i是已创建上下文的递增数字索引。contextOrigin<string> 起源 对应于新创建的用于显示的上下文。其来源应格式化为类似 URL 的形式,但只包含协议、主机和端口(如有必要),例如URL对象的url.origin属性的值。最重要的是,该字符串应省略末尾的斜杠,因为那表示路径。 默认值:''。contextCodeGeneration<Object>cachedData<Buffer> | <TypedArray> | <DataView> 提供一个可选的Buffer、TypedArray或DataView,其中包含 V8 的代码缓存数据,用于提供的源代码。importModuleDynamically<Function> 在评估此模块时被调用,当调用import()时触发。如果未指定此选项,对import()的调用将会被ERR_VM_DYNAMIC_IMPORT_CALLBACK_MISSING拒绝。此选项是实验性模块 API 的一部分。我们不建议在生产环境中使用它。如果未设置--experimental-vm-modules,此回调将被忽略,并且对import()的调用将被ERR_VM_DYNAMIC_IMPORT_CALLBACK_MISSING_FLAG拒绝。specifier<string> 传递给import()的指定符script<vm.Script>importAttributes<Object> 传递给optionsExpression可选参数的"with"值,如果未提供值,则为一个空对象。- 返回:<vm.Module> 建议返回
vm.Module,以便利用错误跟踪,并避免命名空间中包含then函数导出时出现问题。
microtaskMode<string> 如果设置为afterEvaluate,微任务(通过Promise和async function调度的任务)将在脚本运行后立即执行。在这种情况下,它们会包含在timeout和breakOnSigint范围内。
- 返回:<any> 脚本中最后执行的语句的结果。
vm.runInNewContext() 首先将给定的 contextObject 上下文化(如果传入 undefined,则会创建一个新的 contextObject),然后编译 code,在创建的上下文中运行它,最后返回结果。运行代码时无法访问本地作用域。
【The vm.runInNewContext() first contextifies the given contextObject (or
creates a new contextObject if passed as undefined), compiles the code,
runs it within the created context, then returns the result. Running code
does not have access to the local scope.】
如果 options 是字符串,那么它指定文件名。
【If options is a string, then it specifies the filename.】
下面的示例编译并执行代码,该代码会递增一个全局变量并设置一个新的变量。这些全局变量包含在 contextObject 中。
【The following example compiles and executes code that increments a global
variable and sets a new one. These globals are contained in the contextObject.】
const vm = require('node:vm');
const contextObject = {
animal: 'cat',
count: 2,
};
vm.runInNewContext('count += 1; name = "kitty"', contextObject);
console.log(contextObject);
// Prints: { animal: 'cat', count: 3, name: 'kitty' }
vm.runInThisContext(code[, options])#>
code<string> 用于编译和运行的 JavaScript 代码。options<Object> | <string>filename<string> 指定此脚本生成的堆栈跟踪中使用的文件名。默认值:'evalmachine.<anonymous>'。lineOffset<number> 指定此脚本生成的堆栈追踪中显示的行号偏移量。默认值:0。columnOffset<number> 指定此脚本生成的堆栈跟踪中显示的首行列号偏移量。默认值:0。displayErrors<boolean> 当设置为true时,如果在编译code时发生Error错误,导致错误的代码行会附加到堆栈跟踪中。默认值:true。timeout<integer> 指定执行code前的毫秒数,然后终止执行。如果执行被终止,将抛出Error。该值必须是严格正整数。breakOnSigint<boolean> 如果为true,接收到SIGINT(Ctrl+C)将终止执行并抛出一个Error。通过process.on('SIGINT')添加的现有事件处理程序在脚本执行期间被禁用,但之后继续有效。默认值:false。cachedData<Buffer> | <TypedArray> | <DataView> 提供一个可选的Buffer、TypedArray或DataView,其中包含 V8 的代码缓存数据,用于提供的源代码。importModuleDynamically<Function> 在评估此模块时被调用,当调用import()时触发。如果未指定此选项,对import()的调用将会被ERR_VM_DYNAMIC_IMPORT_CALLBACK_MISSING拒绝。此选项是实验性模块 API 的一部分。我们不建议在生产环境中使用它。如果未设置--experimental-vm-modules,此回调将被忽略,并且对import()的调用将被ERR_VM_DYNAMIC_IMPORT_CALLBACK_MISSING_FLAG拒绝。specifier<string> 传递给import()的指定符script<vm.Script>importAttributes<Object> 传递给optionsExpression可选参数的"with"值,如果未提供值,则为一个空对象。- 返回:<vm.Module> 建议返回
vm.Module,以便利用错误跟踪,并避免命名空间中包含then函数导出时出现问题。
- 返回:<any> 脚本中最后执行的语句的结果。
vm.runInThisContext() 会编译 code,在当前 global 的上下文中运行它,并返回结果。运行的代码无法访问本地作用域,但可以访问当前的 global 对象。
如果 options 是字符串,那么它指定文件名。
【If options is a string, then it specifies the filename.】
下面的示例说明了如何同时使用 vm.runInThisContext() 和 JavaScript 的 eval() 函数来运行相同的代码:
【The following example illustrates using both vm.runInThisContext() and
the JavaScript eval() function to run the same code:】
const vm = require('node:vm');
let localVar = 'initial value';
const vmResult = vm.runInThisContext('localVar = "vm";');
console.log(`vmResult: '${vmResult}', localVar: '${localVar}'`);
// Prints: vmResult: 'vm', localVar: 'initial value'
const evalResult = eval('localVar = "eval";');
console.log(`evalResult: '${evalResult}', localVar: '${localVar}'`);
// Prints: evalResult: 'eval', localVar: 'eval'
因为 vm.runInThisContext() 无法访问本地作用域,localVar 不会改变。相比之下,eval() 确实可以访问本地作用域,因此 localVar 的值会改变。以这种方式来看,vm.runInThisContext() 很像 间接 eval() 调用,例如 (0,eval)('code')。
【Because vm.runInThisContext() does not have access to the local scope,
localVar is unchanged. In contrast, eval() does have access to the
local scope, so the value localVar is changed. In this way
vm.runInThisContext() is much like an indirect eval() call, e.g.
(0,eval)('code').】
示例:在虚拟机中运行 HTTP 服务器#>
【Example: Running an HTTP server within a VM】
使用 script.runInThisContext() 或 vm.runInThisContext() 时,代码将在当前 V8 全局上下文中执行。传递给此 VM 上下文的代码将拥有自己独立的作用域。
【When using either script.runInThisContext() or
vm.runInThisContext(), the code is executed within the current V8 global
context. The code passed to this VM context will have its own isolated scope.】
为了使用 node:http 模块运行一个简单的网页服务器,传递给上下文的代码必须自己调用 require('node:http'),或者被传递对 node:http 模块的引用。例如:
【In order to run a simple web server using the node:http module the code passed
to the context must either call require('node:http') on its own, or have a
reference to the node:http module passed to it. For instance:】
'use strict';
const vm = require('node:vm');
const code = `
((require) => {
const http = require('node:http');
http.createServer((request, response) => {
response.writeHead(200, { 'Content-Type': 'text/plain' });
response.end('Hello World\\n');
}).listen(8124);
console.log('Server running at http://127.0.0.1:8124/');
})`;
vm.runInThisContext(code)(require);
上例中的 require() 与传入它的上下文共享状态。当执行不受信任的代码时,这可能会引入风险,例如以不希望的方式修改上下文中的对象。
【The require() in the above case shares the state with the context it is
passed from. This may introduce risks when untrusted code is executed, e.g.
altering objects in the context in unwanted ways.】
给一个对象“上下文化”是什么意思?#>
【What does it mean to "contextify" an object?】
在 Node.js 中执行的所有 JavaScript 都运行在一个“上下文”的范围内。 根据 V8 嵌入指南:
【All JavaScript executed within Node.js runs within the scope of a "context". According to the V8 Embedder's Guide:】
在 V8 中,context 是一种执行环境,它允许在单个 V8 实例中运行独立、无关的 JavaScript 应用。你必须明确指定希望运行任何 JavaScript 代码的上下文。
当调用方法 vm.createContext() 时,contextObject 参数(如果 contextObject 为 undefined,则使用新创建的对象)会在内部与一个新的 V8 上下文实例相关联。这个 V8 上下文为使用 node:vm 模块方法运行的 code 提供了一个隔离的全局环境,使其可以在其中操作。创建 V8 上下文并将其与 contextObject 关联的过程,就是本文档所称的“将对象上下文化”。
【When the method vm.createContext() is called, the contextObject argument
(or a newly-created object if contextObject is undefined) is associated
internally with a new instance of a V8 Context. This V8 Context provides the
code run using the node:vm module's methods with an isolated global
environment within which it can operate. The process of creating the V8 Context
and associating it with the contextObject is what this document refers to as
"contextifying" the object.】
与异步任务和 Promises 的超时交互#>
【Timeout interactions with asynchronous tasks and Promises】
Promise 和 async function 可以让 JavaScript 引擎异步调度任务。默认情况下,这些任务会在当前调用栈上的所有 JavaScript 函数执行完毕后运行。这使得可以绕过 timeout 和 breakOnSigint 选项的功能。
例如,以下由 vm.runInNewContext() 执行的代码,在 5 毫秒的超时时间内,会在一个 promise 解决后安排一个无限循环运行。这个被安排的循环永远不会被超时中断:
【For example, the following code executed by vm.runInNewContext() with a
timeout of 5 milliseconds schedules an infinite loop to run after a promise
resolves. The scheduled loop is never interrupted by the timeout:】
const vm = require('node:vm');
function loop() {
console.log('entering loop');
while (1) console.log(Date.now());
}
vm.runInNewContext(
'Promise.resolve().then(() => loop());',
{ loop, console },
{ timeout: 5 },
);
// This is printed *before* 'entering loop' (!)
console.log('done executing');
可以通过向创建 Context 的代码传递 microtaskMode: 'afterEvaluate' 来解决这个问题:
【This can be addressed by passing microtaskMode: 'afterEvaluate' to the code
that creates the Context:】
const vm = require('node:vm');
function loop() {
while (1) console.log(Date.now());
}
vm.runInNewContext(
'Promise.resolve().then(() => loop());',
{ loop, console },
{ timeout: 5, microtaskMode: 'afterEvaluate' },
);
在这种情况下,通过 promise.then() 调度的微任务将在从 vm.runInNewContext() 返回之前运行,并会被 timeout 功能中断。这仅适用于在 vm.Context 中运行的代码,例如 vm.runInThisContext() 不支持此选项。
【In this case, the microtask scheduled through promise.then() will be run
before returning from vm.runInNewContext(), and will be interrupted
by the timeout functionality. This applies only to code running in a
vm.Context, so e.g. vm.runInThisContext() does not take this option.】
Promise 回调会进入它们被创建的上下文的微任务队列。例如,如果在上述示例中将 () => loop() 替换为仅 loop,那么 loop 将被推入全局微任务队列,因为它是来自外部(主)上下文的函数,因此也能够逃脱超时。
【Promise callbacks are entered into the microtask queue of the context in which
they were created. For example, if () => loop() is replaced with just loop
in the above example, then loop will be pushed into the global microtask
queue, because it is a function from the outer (main) context, and thus will
also be able to escape the timeout.】
如果在 vm.Context 中提供异步调度函数,例如 process.nextTick()、queueMicrotask()、setTimeout()、setImmediate() 等,传递给它们的函数将被添加到全局队列中,而这些队列是所有上下文共享的。因此,传递给这些函数的回调也无法通过超时来控制。
【If asynchronous scheduling functions such as process.nextTick(),
queueMicrotask(), setTimeout(), setImmediate(), etc. are made available
inside a vm.Context, functions passed to them will be added to global queues,
which are shared by all contexts. Therefore, callbacks passed to those functions
are not controllable through the timeout either.】