【JavaScript】 失去了eval时怎么办(表达式解析与求值深入解析)

JavaScript 表达式解析与求值深入解析

有的时候我们不得不面临不可使用eval函数或者new function,但是又需要将一个字符串作为代码运行的尴尬场景,比如小程序考虑到其安全性问题,就禁止使用;这种情况下我们就需要一个表达式解析器来实现功能了。

我们将以一个逻辑运算表达式解析器为例,深入探讨 JavaScript 如何进行表达式解析与求值。在很多计算场景中,特别是需要动态公式处理的情况下,合理地解析和求解运算表达式是至关重要的。我们将通过一个具体的代码示例,一步步展示如何对逻辑运算表达式进行标记化、解析,并最终计算运算结果。

表达式标记化(Tokenize)

表达式的标记化是处理字符串表达式的第一步,旨在将其分解为可理解的单元或"token"。如下所示的 tokenize 函数利用正则表达式匹配各种符号——变量、数字、操作符。

function tokenize(expression) {const tokens = expression.match(/\>=|\<=|\>|\<|\===|\!==|\==|\!=|&&|\|\||\!|\(|\)|-?\d+(\.\d+)?|\b\w+\b/g) || [];return tokens.map(token => {if (token.match(/^\b\w+\b$/)) {return { type: "VARIABLE", value: token };} else if (token.match(/^-?\d+(\.\d+)?$/)) {return { type: "NUMBER", value: Number(token) };} else {return { type: "OPERATOR", value: token };}});
}

期望值确认(Expect)

在解析过程中,确保追踪到期望类型的 token 是有益的。expect 函数检查 token 的类型是否与预期匹配,并处理任何不符合预期的情况。

function expect(tokens, expectedType, expectedValue) {if (tokens.length === 0) throw new Error("Unexpected end of expression");let token = tokens.shift();if (token.type !== expectedType || (expectedValue !== undefined && token.value !== expectedValue)) {throw new Error(`Expected ${expectedType} but found ${token.type}`);}return token;
}

表达式解析(Parse)

在标记化之后的步骤是解析。解析过程通过递归将输入的 token 序列转换成语法树结构,在本例逻辑运算的需求下,我们主要需要考虑的是各种逻辑运算符号的执行优先级,以及有括号()的情况下优先执行括号内内容,因此使用了递归下降解析算法:这种算法是一种自顶向下的解析方法,主要通过递归的方式处理各种优先级的表达式和结构。

下面的 parseExpression 函数示范如何实现。

function parseExpression(tokens) {return parseOr();function parseOr() {let left = parseAnd();while (tokens[0] && tokens[0].value === '||') {tokens.shift();let right = parseAnd();left = left || right;}return left;}function parseAnd() {let left = parseComparison();while (tokens[0] && tokens[0].value === '&&') {tokens.shift();let right = parseComparison();left = left && right;}return left;}function parseComparison() {let left = parseAddition();while (tokens[0] && ['>', '<', '>=', '<=', '===', '!==', '==', '!='].includes(tokens[0].value)) {let op = tokens.shift().value;let right = parseAddition();switch (op) {case '>':left = left > right;break;case '<':left = left < right;break;case '>=':left = left >= right;break;case '<=':left = left <= right;break;case '===':left = left === right;break;case '!==':left = left !== right;break;case '==':left = left == right;break;case '!=':left = left != right;break;}}return left;}function parseAddition() {return parsePrimary();}function parsePrimary() {if (tokens.length === 0) {throw new Error("Unexpected end of expression");}let token = tokens.shift();if (token.type === "NUMBER" || token.type === "VARIABLE") {return token.value;} else if (token.type === "OPERATOR" && token.value === "(") {//处理括号let value = parseExpression(tokens);expect(tokens, "OPERATOR", ")");return value;} else {throw new Error("Invalid syntax");}}}

求值(Evaluate)

最后,表达式求值的过程涉及到实际执行前面得到的语法树,并计算最终结果。evaluate 函数获取一个 token 列表并调用 parseExpression 函数来求值。

function evaluate(expression, variables) {let tokens = tokenize(expression);return parseExpression(tokens, variables);
}

实际应用

在以下的应用中,我们演示了如何使用上述定义的函数来对一个含有逻辑条件的表达式进行求值:

const formula = "{ANALYSIS_ZHKKLRL}<5 && {ANALYSIS_ZHKKLRL}>=0";
const variables = {ANALYSIS_ZHKKLRL: -2.345,
};
const result = evaluate(parseAndEvaluate(formula, variables));
console.log(result);

evaluate 调用中,传入了变量ANALYSIS_ZHKKLRL的值为 -2.345。根据公式中的条件逻辑,结果将输出表达式求值的结果。

结论

如上所述,理解并实现了表达式的标记化、解析和求值过程不仅对于实现复杂的计算功能至关重要,也为开发者提供更多构建灵活和强大应用程序的途径。JavaScript 凭借其灵活性和强大的内置对象,使得我们可以相对简单地实现这些功能。希望通过这篇博客,你能对 JavaScript 表达式的处理有一个更清晰的理解,并在你自己的项目中实施它。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/634129.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

全网最详细!!Python 爬虫快速入门

1. 背景 最近在工作中有需要使用到爬虫的地方&#xff0c;需要根据 Gitlab Python 实现一套定时爬取数据的工具&#xff0c;所以借此机会&#xff0c;针对 Python 爬虫方面的知识进行了学习&#xff0c;也算 Python 爬虫入门了。 需要了解的知识点&#xff1a; Python 基础语…

Spring Cloud核心组件介绍

三大门派 有Spring Cloud的地方就有江湖&#xff0c;我们就来看一看在这个江湖中都有哪些独霸一方的门派! Netflix 是先有SpringCloud还是先有Netflix?这是一个好问题。Netflix是一家大名鼎鼎的互联网传媒公司&#xff0c;但为什么它在开源软件领域有这么大的名声呢?这就…

标题:智能对话新纪元:ChatGPT与文心一言的全面比较

标题&#xff1a;智能对话新纪元&#xff1a;ChatGPT与文心一言的全面比较 引言&#xff1a; 在这个由数据驱动的时代&#xff0c;人工智能语言模型已成为技术创新的前沿。特别是OpenAI的ChatGPT和百度的文心一言&#xff08;ERNIE Bot&#xff09;&#xff0c;它们不仅代表了…

如何在 Element Plus 中使用自定义 icon 组件 (非组件库内置icon)

先说原理就是将 svg 文件以 vue 组件文件的方式使用 需求&#xff1a;我想要在 Element Plus 得评分组件中使用自定义得图标。 el-rate v-model"value1" /> 组件本身是支持自定义图标的&#xff0c;但是教程中只说明了如何使用 element-plus/icons-vue 图标库内置…

常用的gpt-4 prompt words收集2

1. attended an English speaking class about traffic. 学习了关于…的英语口语课 2. I am a newbie at English. 我英语很菜 3. Please respond in Markdown format text. 请返回markdown格式的回答 4. That’s brilliant! / That’s the best idea I’ve heard all da…

vue3移动端调用手机摄像头实现扫描二维码功能

vue3移动端调用手机摄像头实现扫描二维码功能 需求&#xff1a; vue3vant4 实现移动端网页调用手机摄像头实现扫描二维码&#xff0c;并返回二维码附带信息功能 效果图&#xff1a; 实现方法 采用vue3-qr-reader插件实现 项目安装依赖&#xff1a; npm install --save vue3-…

慢查询定位

慢查询 使用工具 mysql自带慢日志 默认没有开启需要手动开启 查看慢日志中的文件 总结

第三讲_ArkTS的初识

ArkTS的初识 1. ArkTS的基本组成2. ArkTS自定义组件 1. ArkTS的基本组成 装饰器&#xff1a; 用于装饰类、结构、方法以及变量&#xff0c;并赋予其特殊的含义。自定义组件&#xff1a;可复用的UI单元&#xff0c;可组合其他组件&#xff0c;图示中Component装饰的struct Hello…

路由综合实验-nat

一.要求 R2为ISP路由器&#xff0c;其上只能配置ip地址&#xff0c;不得再进行其他的任何配置 PC1-PC2可以ping通客户平板和DNS服务器; 客户端可以通过域名访问http1&#xff0c;通过地址访问HTTP2 R1为边界路由器&#xff0c;!其上只有一个公有ip地址 拓扑图&#xff1a; 子…

php怎么获取图片四个角的坐标 x y

使用PHP GD库来处理图像,记得查看是否安装 代码&#xff1a; <?php // 1. 加载图像文件 $image imagecreatefromjpeg(path/to/your/image.jpg); // 根据实际情况修改路径和格式// 2. 获取图像宽度和高度 $width imagesx($image); $height imagesy($image);// 或者直接…

代码随想录day10:栈与队列part01(用栈实现队列,用队列实现栈)

栈与队列part01 用栈实现队列 时间复杂度: push和empty为O(1), pop和peek为O(n) 空间复杂度: O(n) class MyQueue { public:stack<int> stIn; // 输入栈stack<int> stOut; // 输出栈MyQueue() {}void push(int x) {stIn.push(x);}int pop() {// 只有当输出栈为…

Visual SVN Server实战

文章目录 一、实战概述二、实战步骤&#xff08;一&#xff09;下载Visual SVN Server&#xff08;二&#xff09;安装Visual SVN Server&#xff08;三&#xff09;使用Visual SVN Server1、新建仓库&#xff08;1&#xff09;新建Repository&#xff08;2&#xff09;选择仓库…

eNSP学习——配置通过Telnet登陆系统

实验内容&#xff1a; 模拟公司网络场景。R1是机房的设备&#xff0c;办公区与机房不在同一楼层&#xff0c;R2和R3模拟员工主机&#xff0c; 通过交换机S1与R1相连。 为了方便用户的管理&#xff0c;需要在R1上配置Telnet使员工可以在办公区远程管理机房设备。 为…

批量重命名软件,文件夹批量重命名

有时候为了整理或统一格式&#xff0c;我们需要对多个文件夹进行重命名。传统的重命名方式是一个一个来&#xff0c;既费时又费力。如果你还在用这种方式&#xff0c;那么你真的OUT了&#xff01;现在&#xff0c;有一个强大的工具可以帮你批量重命名多个文件夹&#xff0c;甚至…

匿名/箭头函数,立即执行函数IIFE;函数声明式和函数表达式

目录 匿名/箭头函数&#xff1a;简洁 继承上一层作用域链的this 不绑定arguments,用rest参数 rest 参数&#xff1a;...真正的数组 因为没有function声明&#xff0c;所以没有原型prototype&#xff0c;所以不能作为构造函数 当函数体只有一句时&#xff0c;可省 return ,…

Python常用的自动化小脚本!

一、list转json、string转json 可以使用Python内置的json模块将列表(List)和字符串(String)转换成JSON格式。 List转JSON假设我们有一个列表(List)&#xff1a;my_list [apple, banana, cherry] 我们可以使用json.dumps()函数将该列表转换成JSON格式&#xff1a;import json…

94、计算多边形面积

from shapely.geometry import Polygonclass Point:def __init__(self, x, y):self.x xself.y ydef __repr__(self):return str([self.x, self.y])#多边形标准化 def polygonTransform(P):return [Point(p[0], p[1]) for p in P]#计算多边形的面积官方 def calculateAreaOffi…

EMI抑制的方法

1 EMI抑制 抑制电磁干扰&#xff08;EMI&#xff09;是在电子电路和系统设计中非常重要的一项任务&#xff0c;以确保设备的正常运行并遵守电磁兼容性&#xff08;EMC&#xff09;标准。以下是一些常见的方法&#xff0c;用于在电子系统中进行EMI抑制&#xff1a; 滤波器的使…

安装JDK: 错误1316.指定的账户已存在

安装JDK&#xff1a; 错误1316.指定的账户已存在 引方案尝试JDK卸载重装JDK注册表清理JDK21JDK1.8 解压版JDK1.8 8u3xx 引 在执行了某个神秘脚本后&#xff0c;我电脑的很多软件就不可用了&#xff0c;怀疑是注册表被动到了&#xff0c;包括java开发必备的JDK&#xff0c;也无…

TortoiseSVN客户端如何安装配置并实现公网访问服务端提交文件到本地服务器

文章目录 前言1. TortoiseSVN 客户端下载安装2. 创建检出文件夹3. 创建与提交文件4. 公网访问测试 前言 TortoiseSVN是一个开源的版本控制系统&#xff0c;它与Apache Subversion&#xff08;SVN&#xff09;集成在一起&#xff0c;提供了一个用户友好的界面&#xff0c;方便用…