应避免使用eval,改用function构造函数创建隔离作用域:通过参数注入context变量,以eval执行脚本并加script=undefined防护,实现安全局部作用域,但不构成完整沙箱。

JavaScript 中 eval 默认在调用它的词法作用域中执行,无法直接指定变量上下文。要安全、可控地处理作用域问题,核心思路是**避免直接使用 eval,改用 Function 构造函数创建隔离的局部作用域**。
为什么不能直接控制 eval 的作用域
eval 是语法关键字而非普通函数,不支持 call/apply 绑定 this 或作用域;eval(script).call(context) 无效。间接调用(如 let e = eval; e('x=1'))会退化到全局作用域,且行为跨浏览器不一致。with 语句虽能临时扩展作用域链,但已被严格模式禁用,且存在性能和安全缺陷,不可用。
推荐方案:Function 构造函数 + 参数注入
利用 Function 构造函数动态生成一个新函数,把 context 中的键作为形参、值作为实参传入,再在函数体内调用 eval。这样所有变量天然落在该函数的局部作用域内,完全隔离外部环境:
- 提取 context 对象的 key(作为参数名)和 value(作为实参)
- 构造 Function:参数列表为 keys.concat('script'),函数体为 return eval("script = undefined;" + script)
- 关键防护:script = undefined; 防止用户脚本篡改 script 参数本身,避免逻辑绕过
- 调用时传入 values 和原始 script 字符串
实际代码结构
示例实现:
function evalWithContext(script, context = {}) {
const keys = Object.keys(context);
const values = Object.values(context);
const evaluator = Function(
keys,
'script',
'return eval("script = undefined;" + script);'
);
return evaluator(values, script);
}
<p>// 使用
const result = evalWithContext('x * y + z', { x: 2, y: 3, z: 1 });
console.log(result); // 7
</p>
该方式支持任意 JS 类型(对象、函数、Symbol),无需 JSON 序列化,也不污染全局或外层变量。
仍需注意的关键边界
这个方案解决的是作用域隔离问题,不是沙箱:
- script 字符串内容仍可执行任意 JS(如 fetch、localStorage、alert),若来源不可信,必须配合 VM2、Web Worker 等真正隔离环境
- 每次调用都会新建函数,高频使用有性能开销,可考虑缓存 evaluator(当 script 模板固定、仅变量变化时)
- 不要用于解析用户提交的完整逻辑块,优先考虑 JSON.parse、自定义表达式解析器等更受限的替代方案
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











