esm模块不干预eval的动态作用域,eval始终绕过模块边界直接访问调用时的执行上下文;其作用域隔离仅静态有效,不提供运行时沙箱或防护机制。

ESM 模块本身不处理 eval() 生成的动态作用域——它既不参与、也不干预,更不提供隔离或防护。关键点在于:ESM 的作用域隔离能力对 eval 完全无效,eval 始终绕过模块边界,直接穿透到其调用时所在的执行上下文。
ESM 的模块级隔离是静态且被动的
ESM 通过词法作用域天然实现变量私有化:模块顶层声明的 let、const、function 默认不出现在全局,也不会被其他模块直接访问。但这种隔离只在“模块加载与执行阶段”起作用,且仅限于模块自身代码的静态结构。
- 它不创建运行时沙箱,不拦截或重写
eval - 它不修改 JavaScript 引擎的作用域链查找规则
- 模块内调用
eval('x = 42'),仍会按标准规则查找并赋值——可能污染当前函数、外层闭包,甚至全局(非严格模式下)
eval 在 ESM 中的行为与 CommonJS 或脚本环境一致
无论代码写在 .mjs 文件还是 type="module" 的 script 标签里,eval 的执行逻辑不变:
- 直接调用
eval(...)→ 使用当前执行上下文(可能是模块顶层、函数内、或块级作用域) - 间接调用(如
(0, eval)('...'))→ 回退到全局作用域,但该全局仍是浏览器的window或 Node.js 的globalThis,不是模块作用域 - ESM 的
import.meta或export等特性,eval无法直接访问或操作
所谓“防泄露”需靠开发者主动切断连接
ESM 不提供自动防护,但可配合以下方式降低风险:
-
避免在模块顶层或敏感闭包中使用
eval:尤其不要让它靠近私有状态(如let token = 'xxx') -
用
new Function()替代(若必须动态执行):它不继承调用者作用域,只链接全局,天然隔绝模块内局部变量 -
显式清空上下文再执行:例如封装成
function safeRun(code) { return new Function('return (' + code + ')')(); },确保无外部变量可读写 -
输入强校验 + 白名单机制:不拼接用户输入,改用预定义函数映射(如
operators[expr.op]?.(expr.a, expr.b))
真正安全的动态执行,依赖的是机制而非模块格式
想让动态代码不泄露模块状态,不能指望 ESM “替你挡住”,而要靠:
- 不用
eval—— 99% 的场景可用JSON.parse、配置驱动、表达式编译器替代 - 必须执行时,用
new Function并限制参数,或引入专用沙箱(如vm2在 Node.js,iframe+postMessage在浏览器) - 敏感数据不暴露在可被
eval触达的作用域链上(比如不要把apiToken声明在调用eval的同一函数内)











