- 断言测试
- 异步上下文跟踪
- 异步钩子
- 缓冲(Buffer)
- C++ 插件
- 使用 Node-API 的 C/C++ 插件
- C++ 嵌入 Node环境
- 子进程(Child processes)
- 集群(Cluster)
- 命令行选项
- 控制台(Console)
- 核心包(Corepack)
- 加密(Crypto)
- 调试器(Debugger)
- 已弃用的 API
- 诊断通道(Diagnostics Channel)
- 域名系统(DNS)
- 域(Domain)
- 错误(Errors)
- 事件(Events)
- 文件系统(File system)
- 全局变量(Globals)
- HTTP
- HTTP/2
- HTTPS
- 检查器(Inspector)
- 国际化
- 模块:CommonJS 模块
- 模块:ECMAScript 模块
- 模块:
node:moduleAPI - 模块:packages 模块
- 网络(Net)
- 系统(OS)
- 路径(Path)
- 性能挂钩(Performance hooks)
- 性能挂钩(Permissions)
- 进程(Process)
- Punycode 国际化域名编码
- 查询字符串(Query strings)
- 命令行库(Readline)
- REPL 交互式编程环境
- 诊断报告
- 单个可执行应用程序
- Stream 流
- 字符串解码器
- 单元测试
- 定时器(Timers)
- 传输层安全/SSL
- 跟踪事件
- TTY
- UDP/数据报
- URL
- 实用程序
- V8
- 虚拟机
- WebAssembly
- Web加密 API(Web Crypto API)
- 网络流 API(Web Streams API)
- 工作线程(Worker threads)
- zlib
Node.js v18.18.2 文档
- Node.js v18.18.2
-
►
目录
- VM(执行 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])- 示例:在 VM 中运行 HTTP 服务器
- “关联”一个对象是什么意思?
- 与异步任务和 Promise 的超时交互
- 类:
- VM(执行 JavaScript)
-
►
索引
- 断言测试
- 异步上下文跟踪
- 异步钩子
- 缓冲(Buffer)
- C++ 插件
- 使用 Node-API 的 C/C++ 插件
- C++ 嵌入 Node环境
- 子进程(Child processes)
- 集群(Cluster)
- 命令行选项
- 控制台(Console)
- 核心包(Corepack)
- 加密(Crypto)
- 调试器(Debugger)
- 已弃用的 API
- 诊断通道(Diagnostics Channel)
- 域名系统(DNS)
- 域(Domain)
- 错误(Errors)
- 事件(Events)
- 文件系统(File system)
- Globals
- HTTP
- HTTP/2
- HTTPS
- Inspector
- Internationalization
- Modules: CommonJS modules
- Modules: ECMAScript modules
- Modules:
node:moduleAPI - Modules: Packages
- Net
- 系统(OS)
- 路径(Path)
- Performance hooks
- Permissions
- 进程(Process)
- Punycode
- Query strings
- 命令行库(Readline)
- REPL 交互式编程环境
- Report
- Single executable applications
- Stream
- String decoder
- Test runner
- Timers
- TLS/SSL
- Trace events
- TTY
- UDP/datagram
- URL
- Utilities
- V8
- VM
- WASI
- Web Crypto API
- Web Streams API
- Worker threads
- Zlib
- ► 其他版本
- ► 选项
目录
- VM(执行 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])- 示例:在 VM 中运行 HTTP 服务器
- “关联”一个对象是什么意思?
- 与异步任务和 Promise 的超时交互
- 类:
VM(执行 JavaScript)#
源代码: lib/vm.js
node:vm模块支持在 V8 虚拟机上下文中编译和运行代码。
node:vm模块不是安全机制。不要用它来运行不受信任的代码。
JavaScript 代码可以立即编译并运行,也可以稍后编译、保存并运行。
一个常见的用例是在不同的 V8 上下文中运行代码。这意味着被调用的代码具有与调用代码不同的全局对象。
人们可以通过将对象置于上下文中来提供上下文。调用的代码将上下文中的任何属性视为全局变量。由调用的代码引起的全局变量的任何更改都会反映在上下文对象中。
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#
vm.Script类的实例包含可以在特定上下文中执行的预编译脚本。
new vm.Script(code[, options])#
code<string>要编译的 JavaScript 代码。options<对象> | <字符串>filename<string>指定此脚本生成的堆栈跟踪中使用的文件名。默认值:'evalmachine.<anonymous>'。lineOffset<number>指定此脚本生成的堆栈跟踪中显示的行号偏移量。默认值:0。columnOffset<number>指定此脚本生成的堆栈跟踪中显示的第一行列号偏移量。默认值:0。cachedData<缓冲区> | <类型化数组> | <DataView>提供可选的Buffer或TypedArray或DataView以及所提供源的 V8 代码缓存数据。提供后,cachedDataRejected值将设置为true或false,具体取决于 V8 是否接受数据。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 的一部分。我们不建议在生产环境中使用它。specifier<string>说明符传递给import()script<vm.Script>importAssertions<Object>传递给optionsExpression可选参数的"assert"值,如果未提供值,则为空对象。- 返回:<模块命名空间对象> | <vm.Module>建议返回
vm.Module,以便利用错误跟踪,并避免包含then函数导出的命名空间出现问题。
如果options是字符串,则它指定文件名。
创建新的vm.Script对象会编译code但不会运行它。编译后的vm.Script可以稍后多次运行。code未绑定到任何全局对象;相反,它在每次运行之前绑定,仅针对该运行。
script.cachedDataRejected#
当提供cachedData来创建vm.Script时,该值将设置为true或false,具体取决于数据接受情况V8。否则该值为undefined。
script.createCachedData()#
- 返回:<缓冲区>
创建可与Script构造函数的
cachedData选项一起使用的代码缓存。返回一个Buffer。该方法可以随时调用任意次数。
Script的代码缓存不包含任何 JavaScript 可观察状态。代码缓存可以安全地与脚本源一起保存并用于多次构造新的Script实例。
Script源中的函数可以标记为延迟编译,并且它们不会在构建Script时编译。这些函数将在第一次调用时进行编译。代码缓存序列化 V8 当前了解的有关Script的元数据,可用于加速未来的编译。
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内的
vm.Script 对象包含的已编译代码并返回结果。运行代码无权访问本地范围。
以下示例编译代码,递增一个全局变量,设置另一个全局变量的值,然后多次执行该代码。全局变量包含在context对象中。
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选项将导致启动新的事件循环和相应的线程,这会产生非零的性能开销。
script.runInNewContext([contextObject[, options]])#
contextObject<Object>将被上下文化的对象。如果undefined,将创建一个新对象。options<对象>displayErrors<boolean>当true时,如果在编译code时发生Error,则导致错误的代码行将附加到堆栈跟踪。默认值:true。timeout<整数>指定在终止执行之前执行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<对象>microtaskMode<string>如果设置为afterEvaluate,微任务(通过Promise和async function调度的任务)将在脚本已运行。在这种情况下,它们包含在timeout和breakOnSigint范围内。
- 返回:<any>脚本中执行的最后一条语句的结果。
首先将给定的contextObject置于上下文中,在创建的上下文中运行vm.Script对象包含的已编译代码,并返回结果。运行代码无权访问本地范围。
以下示例编译设置全局变量的代码,然后在不同的上下文中多次执行该代码。全局变量在每个单独的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对象。
以下示例编译增加global变量的代码,然后多次执行该代码:
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。
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#
此功能仅在启用--experimental-vm-modules命令标志时可用。
vm.Module类提供了在 VM 上下文中使用 ECMAScript 模块的低级接口。它是vm.Script类的对应类
,密切反映了ECMAScript 规范中定义的Module Record 。
然而,与vm.Script不同的是,每个vm.Module对象在创建时就绑定到上下文。对vm.Module对象的操作本质上是异步的,这与vm.Script对象的同步性质相反。使用“异步”函数可以帮助操作vm.Module对象。
使用vm.Module对象需要三个不同的步骤:创建/解析、链接和评估。下面的示例说明了这三个步骤。
该实现位于比ECMAScript 模块加载器更低的级别。尽管计划提供支持,但还无法与加载程序进行交互。
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#
该模块的所有依赖项的说明符。返回的数组被冻结以不允许对其进行任何更改。
对应于ECMAScript 规范中循环模块记录的[[RequestedModules]]字段。
module.error#
如果module.status为'errored',则此属性包含模块在评估期间抛出的异常。如果状态为其他任何值,则访问此属性将导致引发异常。
值undefined不能用于由于与throw undefined;可能存在歧义而未引发异常的情况。
对应于ECMAScript 规范中循环模块记录的[[EvaluationError]]字段。
module.evaluate([options])#
评估模块。
必须在模块链接后调用此函数;否则会拒绝。当模块已经被评估时也可以调用它,在这种情况下,如果初始评估成功结束(module.status是'evaluated'),它将不执行任何操作,或者它将重新执行抛出初始评估导致的异常(module.status是'errored')。
在评估模块时无法调用此方法(module.status为'evaluating')。
对应于ECMAScript 规范中循环模块记录的Evaluate() 具体方法字段。
module.identifier#
当前模块的标识符,在构造函数中设置。
module.link(linker)#
linker<函数>-
specifier<string>所请求模块的说明符:import foo from 'foo'; // ^^^^^ the module specifier -
referencingModule<vm.Module>调用Module对象link()。 -
extra<对象>assert<Object>来自断言的数据:根据 ECMA-262,主机应忽略它们不支持的断言,而不是在存在不支持的断言时触发错误。import foo from 'foo' assert { name: 'value' }; // ^^^^^^^^^^^^^^^^^ the assertion
-
返回:<vm.Module> | < Promise >
-
- 返回:< Promise >
链接模块依赖关系。该方法必须在评估之前调用,并且每个模块只能调用一次。
该函数预计会返回一个Module对象或一个最终解析为Module对象的Promise对象。返回的Module必须满足以下两个不变量:
- 它必须与父级
Module属于同一上下文。 - 其
status不得为'errored'。
如果返回的Module的status是'unlinked' ,则将在返回的Module上递归调用此方法,并提供相同的linker
函数。
link()返回一个Promise ,当所有链接实例解析为有效的Module时,该值将得到解析;如果链接器函数抛出异常或返回异常,则该值将被拒绝无效Module。
链接器函数大致对应于 ECMAScript 规范中实现定义的 HostResolveImportedModule抽象操作,但有一些关键区别:
- 链接器函数允许异步,而 HostResolveImportedModule是同步的。
模块链接期间使用的实际HostResolveImportedModule实现是返回链接期间链接的模块的实现。由于此时所有模块都已完全链接,因此 HostResolveImportedModule实现按照规范完全同步。
对应于ECMAScript 规范中Cyclic Module Record的Link() 具体方法字段。
module.namespace#
模块的命名空间对象。仅当链接 ( module.link() ) 完成后才可用。
对应ECMAScript规范中的GetModuleNamespace抽象操作。
module.status#
模块的当前状态。将是以下之一:
-
'unlinked':module.link()尚未被调用。 -
'linking':module.link()已被调用,但尚未解决链接器函数返回的所有 Promise。 -
'linked':模块已成功链接,并且其所有依赖项均已链接,但尚未调用module.evaluate()。 -
'evaluating':模块正在通过自身或父模块上的module.evaluate()进行评估。 -
'evaluated':模块已成功评估。 -
'errored':模块已被评估,但引发了异常。
除了'errored'之外,此状态字符串对应于规范的
循环模块记录的[[Status]]字段。'errored'对应于
规范中的'evaluated' ,但将[[EvaluationError]]设置为不是undefined的值。
类:vm.SourceTextModule#
此功能仅在启用--experimental-vm-modules命令标志时可用。
- 扩展:<vm.Module>
vm.SourceTextModule类提供ECMAScript 规范中定义的源文本模块记录。
new vm.SourceTextModule(code[, options])#
code<string>要解析的 JavaScript 模块代码optionsidentifier<string>堆栈跟踪中使用的字符串。 默认值:'vm:module(i)',其中i是上下文特定的升序索引。cachedData<缓冲区> | <类型化数组> | <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<导入.元>module<vm.SourceTextModule>
importModuleDynamically<Function>在调用import()时调用此模块。如果未指定此选项,则对import()的调用将被拒绝并返回ERR_VM_DYNAMIC_IMPORT_CALLBACK_MISSING。specifier<string>说明符传递给import()module<vm.Module>importAssertions<Object>传递给optionsExpression可选参数的"assert"值,如果未提供值,则为空对象。- 返回:<模块命名空间对象> | <vm.Module>建议返回
vm.Module,以便利用错误跟踪,并避免包含then函数导出的命名空间出现问题。
创建一个新的SourceTextModule实例。
分配给import.meta对象的属性(作为对象)可能允许模块访问指定的context之外的信息。使用
vm.runInContext()在特定上下文中创建对象。
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()#
- 返回:<缓冲区>
创建可与SourceTextModule构造函数的
cachedData选项一起使用的代码缓存。返回一个Buffer。在评估模块之前,可以多次调用此方法。
SourceTextModule的代码缓存不包含任何 JavaScript 可观察状态。代码缓存可以安全地与脚本源一起保存,并用于多次构造新的SourceTextModule实例。
SourceTextModule源中的函数可以标记为延迟编译,并且它们不会在构建SourceTextModule时编译。这些函数将在第一次调用时进行编译。代码缓存序列化 V8 当前了解的有关
SourceTextModule的元数据,可用于加速未来的编译。
// 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#
此功能仅在启用--experimental-vm-modules命令标志时可用。
- 扩展:<vm.Module>
vm.SyntheticModule类提供WebIDL 规范中定义的综合模块记录。合成模块的目的是提供一个通用接口,用于将非 JavaScript 源公开给 ECMAScript 模块图。
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实例。
分配给此实例导出的对象可能允许模块的导入者访问指定的context之外的信息。使用
vm.runInContext()在特定上下文中创建对象。
syntheticModule.setExport(name, value)#
该方法在模块链接后使用,用于设置导出的值。如果在链接模块之前调用它,则会抛出ERR_VM_MODULE_STATUS错误。
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<对象>filename<string>指定此脚本生成的堆栈跟踪中使用的文件名。默认值:''。lineOffset<number>指定此脚本生成的堆栈跟踪中显示的行号偏移量。默认值:0。columnOffset<number>指定此脚本生成的堆栈跟踪中显示的第一行列号偏移量。默认值:0。cachedData<缓冲区> | <类型化数组> | <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 的一部分,不应被视为稳定。specifier<string>说明符传递给import()function<函数>importAssertions<Object>传递给optionsExpression可选参数的"assert"值,如果未提供值,则为空对象。- 返回:<模块命名空间对象> | <vm.Module>建议返回
vm.Module,以便利用错误跟踪,并避免包含then函数导出的命名空间出现问题。
- 返回:<函数>
将给定的代码编译到提供的上下文中(如果未提供上下文,则使用当前上下文),并将其包装在具有给定params的函数中返回。
vm.createContext([contextObject[, options]])#
contextObject<对象>options<对象>name<string>新创建的上下文的人类可读名称。 默认值:'VM Context i',其中i是所创建上下文的升序数字索引。origin<string> 对应于新创建的用于显示目的的上下文的源。源的格式应类似于 URL,但仅包含方案、主机和端口(如果需要),例如URL对象的url.origin属性的值。最值得注意的是,该字符串应省略尾部斜杠,因为它表示路径。 默认值:''。codeGeneration<对象>microtaskMode<string>如果设置为afterEvaluate,微任务(通过Promise和async function调度的任务)将在脚本已运行完script.runInContext()。在这种情况下,它们包含在timeout和breakOnSigint范围内。
- 返回:<Object>上下文对象。
如果给定contextObject,则vm.createContext()方法将准备该对象,以便它可以在对
vm.runInContext()或script.runInContext()的调用中使用。在此类脚本中,contextObject将是全局对象,保留其所有现有属性,但也具有任何标准
全局对象所具有的内置对象和函数。在 vm 模块运行的脚本之外,全局变量将保持不变。
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 ),则将返回一个新的空上下文对象。
vm.createContext()方法主要用于创建可用于运行多个脚本的单个上下文。例如,如果模拟网络浏览器,该方法可用于创建表示窗口全局对象的单个上下文,然后在该上下文中一起运行所有<script>标记。
所提供的上下文的name和origin通过 Inspector API 可见。
vm.isContext(object)#
如果给定的object对象已使用
vm.createContext()进行上下文关联,则返回 true 。
vm.measureMemory([options])#
测量 V8 已知的内存以及当前 V8 隔离已知的所有上下文或主上下文使用的内存。
options<对象>可选。- 返回:<Promise>如果成功测量内存,则 Promise 将解析为包含有关内存使用情况的信息的对象。否则,它将被拒绝并出现
ERR_CONTEXT_NOT_INITIALIZED错误。
返回的 Promise 可以解析的对象的格式特定于 V8 引擎,并且可能会从一个版本的 V8 更改为下一个版本。
返回的结果与v8.getHeapSpaceStatistics()返回的统计信息不同
,vm.measureMemory()测量的是 V8 引擎当前实例中每个 V8 特定上下文可到达的内存,而v8.getHeapSpaceStatistics()测量当前V8实例中每个堆空间占用的内存。
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<对象> | <字符串>filename<string>指定此脚本生成的堆栈跟踪中使用的文件名。默认值:'evalmachine.<anonymous>'。lineOffset<number>指定此脚本生成的堆栈跟踪中显示的行号偏移量。默认值:0。columnOffset<number>指定在此脚本生成的堆栈跟踪中显示的第一行列号偏移量。默认值:0。displayErrors<boolean>当true时,如果在编译code时发生Error,则导致错误的代码行将附加到堆栈跟踪。默认值:true。timeout<整数>指定在终止执行之前执行code的毫秒数 。如果执行终止, 则会抛出Error。该值必须是严格的正整数。breakOnSigint<boolean>如果true,接收SIGINT( Ctrl+ C) 将终止执行并抛出Error。通过process.on('SIGINT')附加的事件的现有处理程序 在脚本执行期间被禁用,但此后继续工作。默认值:false。cachedData<缓冲区> | <类型化数组> | <DataView>提供可选的Buffer或TypedArray或DataView以及所提供源的 V8 代码缓存数据。importModuleDynamically<Function>在调用import()时调用此模块。如果未指定此选项,则对import()的调用将被拒绝并返回ERR_VM_DYNAMIC_IMPORT_CALLBACK_MISSING。此选项是实验模块 API 的一部分。我们不建议在生产环境中使用它。specifier<string>说明符传递给import()script<vm.Script>importAssertions<Object>传递给optionsExpression可选参数的"assert"值,如果未提供值,则为空对象。- 返回:<模块命名空间对象> | <vm.Module>建议返回
vm.Module,以便利用错误跟踪,并避免包含then函数导出的命名空间出现问题。
- 返回:<any>脚本中执行的最后一条语句的结果。
vm.runInContext()方法编译code ,在contextifiedObject的上下文中运行它,然后返回结果。运行代码无权访问本地范围。contextifiedObject对象之前必须已使用 vm.createContext()方法进行上下文关联。
如果options是字符串,则它指定文件名。
以下示例使用单个 上下文对象编译并执行不同的脚本:
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<对象> | <字符串>filename<string>指定此脚本生成的堆栈跟踪中使用的文件名。默认值:'evalmachine.<anonymous>'。lineOffset<number>指定此脚本生成的堆栈跟踪中显示的行号偏移量。默认值:0。columnOffset<number>指定在此脚本生成的堆栈跟踪中显示的第一行列号偏移量。默认值:0。displayErrors<boolean>当true时,如果在编译code时发生Error,则导致错误的代码行将附加到堆栈跟踪。默认值:true。timeout<整数>指定在终止执行之前执行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<对象>cachedData<缓冲区> | <类型化数组> | <DataView>提供可选的Buffer或TypedArray或DataView以及所提供源的 V8 代码缓存数据。importModuleDynamically<Function>在调用import()时调用此模块。如果未指定此选项,则对import()的调用将被拒绝并返回ERR_VM_DYNAMIC_IMPORT_CALLBACK_MISSING。此选项是实验模块 API 的一部分。我们不建议在生产环境中使用它。specifier<string>说明符传递给import()script<vm.Script>importAssertions<Object>传递给optionsExpression可选参数的"assert"值,如果未提供值,则为空对象。- 返回:<模块命名空间对象> | <vm.Module>建议返回
vm.Module,以便利用错误跟踪,并避免包含then函数导出的命名空间出现问题。
microtaskMode<string>如果设置为afterEvaluate,微任务(通过Promise和async function调度的任务)将在脚本已运行。在这种情况下,它们包含在timeout和breakOnSigint范围内。
- 返回:<any>脚本中执行的最后一条语句的结果。
vm.runInNewContext()首先将给定的contextObject置于上下文中(或者如果作为undefined传递,则创建一个新的contextObject),编译code,在创建的上下文中运行它,然后返回结果。运行代码无权访问本地范围。
如果options是字符串,则它指定文件名。
以下示例编译并执行增加全局变量并设置新变量的代码。这些全局变量包含在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<对象> | <字符串>filename<string>指定此脚本生成的堆栈跟踪中使用的文件名。默认值:'evalmachine.<anonymous>'。lineOffset<number>指定此脚本生成的堆栈跟踪中显示的行号偏移量。默认值:0。columnOffset<number>指定此脚本生成的堆栈跟踪中显示的第一行列号偏移量。默认值:0。displayErrors<boolean>当true时,如果编译code时发生Error,则导致错误的代码行将附加到堆栈跟踪。默认值:true。timeout<整数>指定在终止执行之前执行code的毫秒数 。如果执行终止, 则会抛出Error。该值必须是严格的正整数。breakOnSigint<boolean>如果true,接收SIGINT( Ctrl+ C) 将终止执行并抛出Error。通过process.on('SIGINT')附加的事件的现有处理程序 在脚本执行期间被禁用,但此后继续工作。默认值:false。cachedData<缓冲区> | <类型化数组> | <DataView>提供可选的Buffer或TypedArray或DataView以及所提供源的 V8 代码缓存数据。importModuleDynamically<Function>在调用import()时调用此模块。如果未指定此选项,则对import()的调用将被拒绝并返回ERR_VM_DYNAMIC_IMPORT_CALLBACK_MISSING。此选项是实验模块 API 的一部分。我们不建议在生产环境中使用它。specifier<string>说明符传递给import()script<vm.Script>importAssertions<Object>传递给optionsExpression可选参数的"assert"值,如果未提供值,则为空对象。- 返回:<模块命名空间对象> | <vm.Module>建议返回
vm.Module,以便利用错误跟踪,并避免包含then函数导出的命名空间出现问题。
- 返回:<any>脚本中执行的最后一条语句的结果。
vm.runInThisContext()编译code,在当前global的上下文中运行它并返回结果。运行的代码无权访问本地范围,但可以访问当前的global对象。
如果options是字符串,则它指定文件名。
以下示例说明了如何使用vm.runInThisContext()和 JavaScript eval()函数来运行相同的代码:
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')。
示例:在 VM 中运行 HTTP 服务器#
当使用script.runInThisContext()或
vm.runInThisContext()时,代码在当前 V8 全局上下文中执行。传递到此 VM 上下文的代码将具有其自己的隔离范围。
为了使用node:http模块运行简单的 Web 服务器,传递到上下文的代码必须单独调用require('node:http'),或者引用node:http模块传递给它。例如:
'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()与其传递过来的上下文共享状态。当执行不受信任的代码时,这可能会带来风险,例如以不需要的方式更改上下文中的对象。
“关联”一个对象是什么意思?#
Node.js 中执行的所有 JavaScript 都在“上下文”范围内运行。根据V8 嵌入指南:
在 V8 中,上下文是一个执行环境,允许单独的、不相关的 JavaScript 应用程序在 V8 的单个实例中运行。您必须显式指定您希望运行任何 JavaScript 代码的上下文。
当调用方法vm.createContext()时, contextObject参数(如果contextObject为undefined则为新创建的对象)在内部与V8 上下文的新实例。此 V8 上下文为
使用node:vm 模块方法的 code运行提供了一个可以在其中运行的隔离全局环境。创建 V8 上下文并将其与contextObject关联的过程就是本文档所说的“对象的“上下文化””。
与异步任务和 Promise 的超时交互#
Promise和async function可以安排 JavaScript 引擎异步运行的任务。默认情况下,这些任务在当前堆栈上的所有 JavaScript 函数执行完毕后运行。这允许转义timeout和
breakOnSigint选项的功能。
例如,由vm.runInNewContext()执行的以下代码(超时时间为 5 毫秒)会安排无限循环在 Promise 解析后运行。计划的循环永远不会因超时而中断:
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');
可以通过将microtaskMode: 'afterEvaluate'传递给创建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()不采用此选项。
Promise 回调被输入到创建它们的上下文的微任务队列中。例如,如果
在上例中将() => loop()替换为loop ,则loop将被推入全局微任务队列,因为它是来自外部(主)上下文,因此也能够逃脱超时。
如果process.nextTick()、
queueMicrotask()、setTimeout()、setImmediate()等异步调度函数在vm.Context,传递给它们的函数将被添加到全局队列中,该队列由所有上下文共享。因此,传递给这些函数的回调也无法通过超时进行控制。