vm 虚拟机,vm 模块使能够在 V8 虚拟机上下文中编译和运行代码。
JavaScript 代码可以被立即地编译并且运行,或者编译、保存并且稍后运行。
一个例子
const vm = require('vm');
const code = 'console.log("1");';
const script = new vm.Script(code);
script.runInThisContext();
复用字节码
JavaScript 代码编译成字节码,通过二进制存储起来,稍后运行。
编译成字节码:
const vm = require('vm');
const code = `console.log("1");`;
const script = new vm.Script(code, {
produceCachedData: true // 需要指定 produceCachedData 为 true
});
// script.cachedData 为字节码 buffer
const bytecodeBuffer = script.cachedData;
// 可以将 bytecodeBuffer 保存成一个二进制文件
使用字节码:
// 先获取 bytecodeBuffer
const anotherScript = new vm.Script(' '.repeat(code.length), {
cachedData: bytecodeBuffer // 指定字节码
});
anotherScript.runInThisContext();
注意,这里 vm.Script 第一个参数可以传空字符串,但长度必须和之前的代码长度一致。
创建 vm.Script 实例时,V8 会检查字节码(cachedData)是否与源代码(第一个参数传入的代码)匹配,如果匹配就跳过编译过程,所以第一个参数不能省略。其次,这个检查非常简单,它只会对比代码长度是否一致,所以只要使用与源代码长度相同的空格,就可以“欺骗”这个检查。
runInContext vs runInNewContext vs runInThisContext
首先它们的作用是一样的,执行 vm.Script 对象里被编译的代码并返回其结果,被执行的代码都无法获取本地作用域。
const vm = require('vm');
const code = 'console.log(a);';
const script = new vm.Script(code);
const a = 1; // 本地作用域变量
script.runInThisContext(); // 报错
script.runInContext(); // 报错
script.runInNewContext(); // 报错
runInThisContext
能获取 global 对象。
const vm = require('vm');
const code = 'console.log(a);';
const script = new vm.Script(code);
global.a = 1; // golbal对象变量
script.runInThisContext();
runInContext
无法获取本地作用域,也不能获取 global 对象,但是可以传入一个对象作为上下文 context。
const vm = require('vm');
const code = 'console.log(a);';
const script = new vm.Script(code);
const context = {
a: 1, console
};
vm.createContext(context); // conext需要传给vm.createContext处理
script.runInContext(context);
在code
中,对全局变量进行修改,会体现在 context
对象中:
const vm = require('vm');
const code = 'a++';
const script = new vm.Script(code);
const context = {
a: 1,
};
vm.createContext(context); // conext需要传给vm.createContext处理
script.runInContext(context);
console.log(context.a); // 2
runInNewContext
runInNewContext的源代码如下,它不过是对runInContext调用的简化。
runInNewContext(contextObject, options) {
const context = createContext(contextObject, getContextOptions(options));
return this.runInContext(context, options);
}
例:
const vm = require('vm');
const code = 'console.log(a);';
const script = new vm.Script(code);
const context = {
a: 1, console
};
script.runInNewContext(context); // 因为不能获取 global 对象,所以 console 也需要传入。