eval并非动态作用域,而是直接闯入并读写当前词法作用域的变量,导致污染;它不查找作用域链,却能修改外层let/const/var变量,甚至声明新变量泄漏到函数顶层,而function构造函数则具备独立作用域,可实现作用域隔离。

eval 执行时没有“动态作用域”,它用的是当前词法作用域的快照,但会直接读写这个作用域里的变量——这容易被误认为是“动态作用域”,实际是作用域污染。
eval 怎么访问变量:不是查找,而是“闯入”
eval 不像函数调用那样遵循作用域链逐层向上找;它在执行时,就站在调用它的那个函数内部,能直接看到、修改所有局部变量和外层闭包变量:
- 在函数里调用 eval,它就能读写该函数的所有
let、const、var变量 - 如果函数嵌套,eval 还能访问外层函数的变量(只要没被遮蔽)
- 它甚至能声明新变量:
eval("var x = 100"),这个x就会出现在当前作用域中
污染的具体表现:变量被悄悄改写或泄漏
这种“闯入”能力导致两类常见污染:
-
意外覆盖:比如你写了
let count = 5,eval 里执行"count = count * 2",外部的count立刻变成 10 -
变量泄漏:用
var声明的变量会提升到函数顶层,eval("var temp = 'hello'")让temp在整个函数内都可访问 - 破坏优化:V8 引擎一旦发现函数里有 eval,就会禁用大部分 JIT 优化,因为无法静态确定变量是否被改过
和 Function 构造函数的关键区别
很多人用 new Function(...) 替代 eval,因为它不污染当前作用域:
-
new Function('x', 'return x + 1')创建的函数,只认自己参数和全局变量,完全看不到外层函数的let y = 2 - 它有自己的独立作用域,不会修改调用位置的任何变量
- 虽然仍不安全(执行任意代码),但至少做到了作用域隔离
真正安全的做法:别让 eval 接触敏感上下文
如果非得用字符串执行逻辑,优先考虑:
- 用
Function构造函数传参建沙盒,把所需变量显式当参数传进去 - 对输入脚本做白名单校验(比如只允许数字、运算符、括号和已知变量名)
- 绝对不要把
window、document、localStorage或带原型的对象放进执行环境 - JSON 解析一律用
JSON.parse(),永远不用eval('(' + json + ')')
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











