- 文档/教程
- 前端
【免费下载链接】en.javascript.info
Modern JavaScript Tutorial
eval是 JavaScript 内置的“代码字符串执行器”,本教程(Modern JavaScript Tutorial)在 1-js/99-js-misc/02-eval/article.md 一章中系统讲解了它的执行语义与安全边界,并以 “Eval-calculator” 任务作为实战收尾:用prompt读取一条算术表达式,用eval求值并返回结果。读完本文,你将掌握eval的词法环境规则、严格模式差异,并能独立实现一个最小可用的表达式计算器,同时学会用正则校验等手段规避代码注入风险。
任务背景:Eval-calculator 在教程中的位置
Eval-calculator 是 eval 章节(1-js/99-js-misc/02-eval)配套的练习任务,任务描述位于 1-js/99-js-misc/02-eval/1-eval-calculator/task.md,要点如下:
- 创建一个计算器:用
prompt提示用户输入一条算术表达式,然后返回计算结果; - 明确约定:本任务不要求校验表达式的正确性,直接求值并返回结果即可;
- 任务附带
[demo]演示位,完整的可运行参考实现在同目录的 solution.md。
它被设计为“入门难度”(importance: 4),目的不是展示复杂语法,而是让你亲手体验eval最自然的用途——把运行时才得到的代码字符串变成真正的执行结果。
前置知识:eval 的核心执行语义
在动手写计算器之前,先明确eval的四条关键语义(均来自 eval 章节正文 article.md)。
1. 语法与返回值
let result = eval(code);code是任意 JavaScript 代码字符串,可以很长,可以包含换行、函数声明、变量等;- 返回值是最后一条语句的结果。
let value = eval('1+1'); alert(value); // 2let value = eval('let i = 0; ++i'); alert(value); // 1这正是计算器任务的基石:2*3+2这类算术字符串传入eval,会按 JavaScript 运算符优先级求出数值结果。
2. 在当前词法环境中执行
eval的代码在调用处的词法环境中执行,因此它可以看到(并修改)外部变量:
let a = 1; function f() { let a = 2; eval('alert(a)'); // 2,看到的是 f 内部的 a } f();let x = 5; eval("x = 10"); alert(x); // 10,外部变量被修改3. 严格模式下拥有独立词法环境
在'use strict'下,eval拥有自己的词法环境,内部声明的函数和变量对外不可见:
// 注意:本仓库的可运行示例默认开启 'use strict' eval("let x = 5; function f() {}"); alert(typeof x); // undefined(不存在该变量) // 函数 f 同样不可见而非严格模式下,eval没有独立词法环境,x和f会在外部泄漏可见。
4. “eval is evil”:为什么现代代码几乎不用它
正文明确指出:在现代化的 JavaScript 中,eval的使用极少。原因包括:
- 早年 JS 语言能力弱,很多事只能靠
eval完成,但这个时代早已过去; - 能访问外部变量这一特性带来副作用与维护困难;
- 代码压缩器(minifier)会重命名局部变量以减小体积,但若存在
eval,被 eval 字符串可能访问的变量就不能安全重命名,从而拉低压缩率。
正文给出的建议是:绝大多数eval场景都可以用现代语言结构或 JavaScript Module 替代。以上语义与安全边界,是理解并扩展计算器任务的前提。
参考答案:最小可用的 eval 计算器
任务的参考实现非常精简,完整代码位于 solution.md:
let expr = prompt("Type an arithmetic expression?", '2*3+2'); alert( eval(expr) );逐行拆解:
prompt("Type an arithmetic expression?", '2*3+2'):弹出输入框,默认值预填2*3+2,用户输入如1+2*3、(10-4)/2之类的算术表达式;eval(expr):把用户输入的字符串当作 JavaScript 代码求值。由于算术表达式本身就是合法的 JS 表达式,eval会按运算符优先级正确计算;alert(...):把eval返回的“最后一条语句的结果”弹出显示。
该实现可以直接复制到浏览器控制台或 HTML<script>中运行,配合prompt/alert即可构成一个完整的交互式计算器 demo。
任务边界与风险:为什么“不校验”是刻意的
任务描述特意强调“无需校验表达式正确性”,这不是疏漏,而是教学策略——先用最直接的方式展示eval的价值,把安全话题留到下一步。而参考答案随后明确点出了隐患:
用户可以输入任意文本或任意代码。
例如用户输入alert('hacked')、x = 100甚至while(true){},eval都会忠实地执行它们。这意味着:
- 代码注入:表达式输入框实际上成了一个任意 JS 代码执行入口;
- 外部变量污染:由于
eval在当前词法环境执行,它可以读写外层变量; - 性能与稳定性风险:恶意或误输入的代码可能引发死循环、报错中断等。
这正是 eval 章节正文强调“evil”的现实体现。因此参考答案给出了加固方向(见下文),这也构成了任务的“进阶思考题”。
安全化改造:用正则把输入限制为纯算术
参考答案提出的安全策略是:用正则表达式校验expr,只允许包含数字与运算符。教程中正则相关内容位于 9-regular-expressions/index.md 章节,可结合学习。
一个可行的加固版实现思路如下(在原答案基础上扩展):
// 只允许数字、空格与算术运算符:+ - * / ( ) .(含小数点和科学计数可选) let expr = prompt("Type an arithmetic expression?", '2*3+2'); if (/^[0-9+\-*/().\s]+$/.test(expr)) { alert( eval(expr) ); } else { alert("Only arithmetic expressions are allowed!"); }要点说明:
- 正则
^[0-9+\-*/().\s]+$用^与$锚定整个字符串,从字符层面白名单化:只接受数字、四则运算符、括号、小数点与空白; - 经过校验后,字符串中不可能再携带
alert(...)、=、引号、分号等代码语法成分,从而把eval的执行范围收窄到纯算术求值; - 注意这是“字符级”校验,仍可能放行如
1/0(结果为Infinity)、0*999...等边界输入,但它已足以阻断绝大多数注入场景。
如果想彻底避开eval,教程正文还提供了两条更安全的路径(详见下一节)。
两条更安全的替代路径
eval 章节正文给出了两条规避局部变量访问问题的标准做法,均可在计算器场景中借鉴。
路径一:window.eval(...)—— 把执行推到全局作用域
如果被求值的代码不需要访问外部局部变量,用window.eval调用,代码将在全局作用域执行:
let x = 1; { let x = 5; window.eval('alert(x)'); // 1(全局变量) }此时eval只能看到全局变量,看不到当前块级作用域里的let x = 5,副作用面大幅收窄。
路径二:new Function—— 显式传参,杜绝闭包依赖
如果被求值的代码确实需要外部数据,教程推荐改用new Function并显式传参。new Function的完整语法与闭包行为见 1-js/06-advanced-functions/07-new-function/article.md:
let f = new Function('a', 'alert(a)'); f(5); // 5关键原理:用new Function创建的函数,其内部属性[[Environment]]指向全局词法环境而非创建处的环境,因此它天然看不到外部局部变量,数据只能通过参数显式传入。这比依赖eval隐式读取外层变量清晰得多,也便于维护。
计算器场景的最终形态
把两条路径结合起来,一个“更安全”的计算器可以这样写:
let expr = prompt("Type an arithmetic expression?", '2*3+2'); // 第一步:正则白名单,只放行纯算术字符 if (!/^[0-9+\-*/().\s]+$/.test(expr)) { alert("Only arithmetic expressions are allowed!"); } else { // 第二步:在全局作用域求值,避免接触局部变量 alert( window.eval(expr) ); }(若你的页面运行在 Worker 等无window的环境,可改用globalThis.eval(expr)。)
实战要点速览
- 核心一行:
eval(prompt("Type an arithmetic expression?", '2*3+2'))即是最小可用的表达式计算器,直接体现“eval返回最后一条语句结果”的语义; - 词法环境:非严格模式下
eval可读写调用处的外部变量,严格模式下拥有独立词法环境、内部声明不外泄; - 风险边界:不对输入做任何校验的计算器等同于任意代码执行入口,必须警惕注入;
- 加固手段:正则白名单校验输入字符(参考 9-regular-expressions);需要全局作用域时用
window.eval;需要外部数据时改用new Function显式传参(参考 new Function 章节); - 工程态度:如 eval 章节正文 所总结——现代 JavaScript 中
eval几乎无必要,优先用模块(1-js/13-modules/index.md)与现代语言结构替代;仅在确实需要运行时动态执行代码字符串的极少数场景(如接收服务器端代码、模板动态编译)才考虑它。
以上即为 Eval-calculator 任务的完整解析:从任务描述(task.md)、参考实现(solution.md)到底层语义与安全加固,你可以按此路径在本地直接复现并进一步实验。
- 文档/教程
- 前端
【免费下载链接】en.javascript.info
Modern JavaScript Tutorial
相关推荐
用 eval 构建算术表达式计算器:现代 JavaScript 中 eval 的用法与安全边界
用 eval 构建算术表达式计算器:现代 JavaScript 中 eval 的用法与安全边界 eval 是 JavaScript 内置的"代码字符串执行器",
文档/教程前端JavaScript 计算器对象实战:用 Object Methods 与 this 实现可交互的 calculator(Modern JavaScript Tutorial)
JavaScript 计算器对象实战:用 Object Methods 与 this 实现可交互的 calculator(Modern JavaScript T
文档/教程前端Modern JavaScript Tutorial 实战:用构造器函数打造可扩展的 Calculator 计算器
Modern JavaScript Tutorial 实战:用构造器函数打造可扩展的 Calculator 计算器 本篇技术指南以 Modern JavaScr
文档/教程前端
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考