三种方式对比
| 方式 | 能访问外部作用域 | 安全性 | 推荐场景 |
|---|---|---|---|
eval() | 是 | 低 | 几乎不推荐 |
new Function() | 否 | 中 | 浏览器/Node 通用 |
vm.runInContext() | 否(隔离) | 高 | Node.js 执行用户代码 |
new Function(推荐)
// 无参数
const fn = new Function("return 1 + 2");
console.log(fn()); // 3
// 带参数
const add = new Function("a", "b", "return a + b");
console.log(add(1, 2)); // 3
// 多行代码
const code = `
const x = 10;
return x * 2;
`;
console.log(new Function(code)()); // 20
new Function 创建的函数只能访问全局作用域,不能访问创建它时的局部变量——这是它比 eval 更安全的原因。
eval(不推荐)
const result = eval("1 + 2"); // 3
eval 可以访问当前闭包内的变量,是潜在的代码注入入口,大多数代码规范禁止使用。
注入上下文:with(data)
在表单引擎、规则配置等场景中,需要让动态代码访问对象属性:
function runExpression(expr, data) {
return new Function("data", `with(data){ return ${expr} }`)(data);
}
runExpression("price * count", { price: 10, count: 2 }); // 20
runExpression("amount > 100", { amount: 200 }); // true
with(data) 把 data 的属性提升为词法作用域,表达式中可以直接使用属性名。
执行用户脚本(带函数体)
从数据库加载 JS 函数字符串并执行:
const scriptStr = `
function check(order) {
return order.amount > 100;
}
`;
// 提取函数体,传入参数执行
const fn = new Function("order", `
${scriptStr}
return check(order);
`);
console.log(fn({ amount: 200 })); // true
异步版本:AsyncFunction
// 获取异步函数构造器
const AsyncFunction = Object.getPrototypeOf(async function(){}).constructor;
const code = `
const res = await Promise.resolve(123);
return res;
`;
const fn = new AsyncFunction(code);
fn().then(console.log); // 123
Node.js vm 沙箱(服务端执行用户代码)
vm 模块提供真正的上下文隔离,适合服务端运行不受信任的代码:
const vm = require("vm");
// 创建隔离上下文
const context = { a: 1, b: 2, result: 0 };
vm.createContext(context);
vm.runInContext("result = a + b", context);
console.log(context.result); // 3
设置超时,防止死循环:
try {
vm.runInContext(
"while(true){}",
context,
{ timeout: 1000 } // 1秒超时
);
} catch (e) {
console.log("超时:", e.message);
}
完整执行流程:
function safeExec(code, data, timeoutMs = 500) {
const ctx = { ...data, __result: undefined };
vm.createContext(ctx);
vm.runInContext(
`__result = (function(){ ${code} })()`,
ctx,
{ timeout: timeoutMs }
);
return ctx.__result;
}
safeExec("return a * b", { a: 3, b: 4 }); // 12
vm 模块的局限
vm 不是完全安全的沙箱,能被构造特定代码逃逸(prototype chain escape)。对安全要求更高的场景,使用 vm2 库(已停止维护)或 isolated-vm(推荐):
import ivm from "isolated-vm";
const isolate = new ivm.Isolate({ memoryLimit: 64 });
const ctx = await isolate.createContext();
const result = await ctx.eval("1 + 2");