主题
eval 与动态执行代码
eval() 会把字符串当作 JavaScript 代码执行。它的核心问题不是“能不能用”,而是“几乎总有更安全、更清晰的替代方案”。
eval 是什么
js
eval('console.log(1)')上面这段代码会在运行时解析字符串,并执行其中的内容。
可以先记成一句话:eval 让“字符串”变成“代码”。
为什么它经常被明确禁止
主要有三类问题:
- 安全风险高
- 可维护性差
- 性能和优化表现差
这也是很多项目直接启用 no-eval 规则的原因。
安全风险
如果传入的字符串来源不可信,就等于把执行权交给外部输入:
js
const code = location.search.slice(1)
eval(code)这类写法很容易演变成代码注入、XSS、数据泄露或权限滥用。
可维护性问题
eval 会让代码关系变得不透明:
- 依赖关系无法静态分析
- 重构工具很难正确处理
- 阅读时不容易确认实际执行内容
- 调试和排查成本更高
看到字符串拼代码时,通常就已经在增加维护成本。
性能问题
eval 的代码要在运行时再解析一次,JavaScript 引擎也更难对这类代码做稳定优化。
所以它不只是“不优雅”,通常也“不划算”。
直接调用和间接调用
这是 eval 的一个常见细节。
直接调用:
js
function run() {
let value = 1
eval('value = 2')
console.log(value) // 非严格模式下可能变成 2
}间接调用:
js
const e = eval
e('var count = 1')间接调用不会按“当前局部作用域”来理解,行为边界也更难把握。
严格模式下要更谨慎
严格模式会进一步限制 eval 的行为,但这并不意味着它就变安全了。
可以记住:
- 严格模式不会让
eval变成推荐方案 - 它只是让部分作用域行为没那么混乱
什么时候最容易误用
常见误用包括:
- 用
eval解析 JSON - 拼接字符串做动态调用
- 从服务端拿一段字符串直接执行
- 为了访问对象属性而写动态代码
这些场景通常都能用更普通的写法解决。
更常见的替代方案
解析数据
不要这样:
js
const data = eval('(' + jsonText + ')')应改成:
js
const data = JSON.parse(jsonText)动态调用逻辑
不要拼接代码:
js
eval(`${actionName}(1, 2)`)更适合用映射表:
js
const actions = {
add(a, b) {
return a + b
},
multiply(a, b) {
return a * b
},
}
const result = actions[actionName](1, 2)动态读取属性
不要这样:
js
eval(`user.${key}`)应直接使用属性访问:
js
const value = user[key]有没有可能必须使用
极少数场景里会见到:
- REPL
- 在线代码编辑器
- 受控的脚本执行器
- 明确隔离过的沙箱环境
但这类场景的重点通常不是“会用 eval”,而是“如何隔离执行环境、限制能力、记录审计”。
和 Function 构造器的关系
Function 构造器本质上也是动态执行代码的一种方式:
js
const sum = new Function('a', 'b', 'return a + b')它和 eval 一样,不应因为语法不同就被当成安全替代品。
使用建议
- 默认把
eval视为应避免的能力。 - 看到字符串拼代码时,先想对象映射、普通函数调用、
JSON.parse()或解析器方案。 - 如果真的要动态执行代码,先解决隔离和输入可信度,而不是先写执行逻辑。
- 在业务项目里,优先让 ESLint 直接禁止
eval。
