
本文介绍如何使用 Node.js 的 vm 模块为动态执行的 JavaScript 代码(如字符串形式的模块)注入自定义全局变量(如 myGlobal),解决 _compile 方式无法访问外部作用域变量的问题。
本文介绍如何使用 node.js 的 `vm` 模块为动态执行的 javascript 代码(如字符串形式的模块)注入自定义全局变量(如 `myglobal`),解决 `_compile` 方式无法访问外部作用域变量的问题。
在 Node.js 中,直接通过 module.constructor 和 _compile 动态执行代码时,新模块运行在独立的、隔离的模块作用域中,无法访问父作用域中的变量(如 myGlobal)——这正是原始代码抛出 ReferenceError: myGlobal is not defined 的根本原因。_compile 并非设计用于暴露外部变量,它仅模拟模块加载流程,但不提供上下文注入能力。
正确的解决方案是使用 Node.js 内置的 vm 模块,它专为安全、可控地执行沙箱化 JavaScript 代码而设计,并支持显式传入执行上下文(context)。关键步骤如下:
-
构建上下文对象:将所需变量(如
myGlobal)、module实例、require函数等挂载到一个普通对象上; -
调用
vm.createContext():将该对象转化为可被vm执行的上下文环境; -
编译并运行脚本:使用
vm.Script编译源码,再通过runInContext()在指定上下文中执行。
以下是完整、可运行的示例代码:
const vm = require('node:vm');
(async () => {
const code = `
const got = require('got'); // 注意:需确保 got 已安装且在上下文中可用
module.exports = async (data) => {
return myGlobal + ' ' + data;
}
`;
// ✅ 定义执行上下文:所有属性将成为脚本内的全局变量
const context = {
myGlobal: 'Hello',
module: {}, // 必须提供,否则 module.exports 会报错
require, // 允许脚本内调用 require(注意:受限于当前上下文权限)
};
vm.createContext(context); // 将 context 转为 vm 可识别的上下文
const script = new vm.Script(code);
const result = await script.runInContext(context)('John');
console.log(result); // 输出:Hello John
})();
⚠️ 重要注意事项:
-
vm.createContext()是必需步骤,不可省略;直接将普通对象传给runInContext()会导致TypeError; -
require函数需显式传入上下文,否则脚本中require('xxx')将失败(也可使用vm.Module或context.require做更细粒度控制); -
vm模块不提供完全的 Node.js 全局对象(如process,console,Buffer),如需使用,需手动挂载(例如context.console = console); - 生产环境中慎用
vm执行不可信代码——即使有上下文隔离,仍存在原型污染、内存耗尽等风险,建议配合timeout、memoryLimit(Node.js 20.10+)等选项增强安全性。
总结:当需要为动态字符串模块注入变量或依赖时,应放弃非标准的 _compile 黑盒方式,转而采用语义清晰、功能完备且受官方支持的 vm 模块。它不仅解决了上下文传递问题,还提供了沙箱控制、超时中断和资源限制等企业级能力。











