eval复用调用位置的执行上下文,直接继承其作用域链,可读写外层变量但不泄露内部声明;new function仅绑定全局作用域,无法访问外层局部变量。

JavaScript 中 eval() 的执行上下文机制并非一成不变,而是随着语言规范演进、安全需求提升和引擎优化需要持续调整。它的核心逻辑始终围绕“作用域继承”,但具体行为在不同版本、调用方式和模式下差异显著。
早期:自由继承与对象级 eval 方法
JavaScript 1.0–1.1 时期,eval 是纯粹的全局函数,执行时直接共享调用点的词法环境。更特殊的是,JavaScript 1.1 曾为所有对象添加了 obj.eval() 方法:它不仅让 this 指向当前对象,还隐式引入类似 with(obj) 的查找逻辑——变量优先从对象属性中读取。例如:
-
o = {x: 1}; o.eval('x + 1')返回2(x来自o.x) -
eval('x + 1')则报错(x未定义)
这一特性在 JavaScript 1.2 中被废弃,标志着对动态作用域的初步收敛。
ES5 时代:直接/间接调用分野与严格模式约束
ECMAScript 5 明确区分了 eval 的两种调用形式:
-
直接调用:语法上必须是标识符
eval(...),且不能赋值、重命名或通过属性访问。此时代码运行在当前函数作用域内,可读写局部变量、声明var变量,但let/const声明仅限 eval 内部作用域。 -
间接调用:如
const f = eval; f("...")或window.eval(...)。此时一律进入全局作用域(非严格模式下),所声明变量会泄漏到全局;严格模式下则禁止变量声明,仅允许表达式求值。
严格模式进一步限制:eval 内无法使用 arguments 和 caller,且禁止修改外围函数的参数绑定,强化了作用域隔离。
现代引擎:词法环境固化与优化抑制
当前主流引擎(V8、SpiderMonkey、JavaScriptCore)将 eval 视为“作用域污染源”。一旦函数内出现任何 eval 调用(即使未执行),引擎就会放弃对该函数的多数静态优化,包括:
- 内联函数调用
- 变量逃逸分析
- JIT 编译中的作用域快照固化
这是因为 eval 可能动态创建变量、修改 this、甚至重定义参数——这些行为无法在编译期预判。引擎选择保守策略:将整个函数降级为解释执行或低效 JIT,确保语义正确性。
现状:规范冻结、实践弃用、替代方案成熟
ES2015+ 规范已不再扩展 eval 行为,其上下文规则稳定在 ES5 定义基础上。现实中:
- 现代框架和 Linter(如 ESLint 的
no-eval)默认禁用eval - JSON 解析统一用
JSON.parse(),动态表达式计算倾向Function构造器(作用域隔离更明确)或专用库(如 math.js) - 模板字符串、Proxy、Reflect 等机制提供了更安全、更可控的动态行为替代路径
执行上下文本身没变,但它的存在价值已被更健壮、更可预测的编程模式取代。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











