基于HTML+JQuery开发计算器:无eval()实现表达式计算方案咨询
嘿,我来给你分享几个不用eval()就能实现带括号表达式计算的靠谱思路,刚好适配你的HTML+jQuery计算器需求~
思路1:递归解析+优先级分步计算
这个思路的核心是模拟人类计算表达式的逻辑:先算括号里的,再算乘除,最后算加减,用递归处理嵌套括号。
具体步骤:
- 预处理输入:用jQuery获取输入框的值,先去掉空格,统一运算符格式(比如把中文乘除换成
*//) - 递归处理括号:找到最内层的括号,计算里面的子表达式,把结果替换原括号部分,重复这个过程直到整个表达式没有括号
- 分步处理乘除/加减:先遍历表达式计算所有乘除,再处理剩下的加减
代码示例:
// 核心计算函数 function calculate(expr) { // 先处理所有括号(递归) while (expr.includes('(')) { const closeIdx = expr.indexOf(')'); const openIdx = expr.lastIndexOf('(', closeIdx); const subExpr = expr.slice(openIdx + 1, closeIdx); // 计算子表达式,替换原括号部分 expr = expr.replace(`(${subExpr})`, calculateSubExpr(subExpr)); } // 处理无括号的表达式 return calculateSubExpr(expr); } // 处理无括号的表达式:先乘除后加减 function calculateSubExpr(expr) { // 处理乘除 const multiplyDivideReg = /(\d+(\.\d+)?)([*/])(\d+(\.\d+)?)/; while (multiplyDivideReg.test(expr)) { expr = expr.replace(multiplyDivideReg, (match, num1, _, op, num2) => { const n1 = parseFloat(num1); const n2 = parseFloat(num2); return op === '*' ? n1 * n2 : n1 / n2; }); } // 处理加减 const addSubtractReg = /(-?\d+(\.\d+)?)([+-])(\d+(\.\d+)?)/; while (addSubtractReg.test(expr)) { expr = expr.replace(addSubtractReg, (match, num1, _, op, num2) => { const n1 = parseFloat(num1); const n2 = parseFloat(num2); return op === '+' ? n1 + n2 : n1 - n2; }); } return parseFloat(expr); } // jQuery调用示例 $('#calc-btn').click(function() { const inputVal = $('#calc-input').val().replace(/\s+/g, ''); // 简单输入验证:括号配对、无非法字符 const openCount = (inputVal.match(/\(/g) || []).length; const closeCount = (inputVal.match(/\)/g) || []).length; if (!inputVal || openCount !== closeCount || !/^[\d+\-*/().]+$/.test(inputVal)) { $('#result').text('请输入合法的表达式'); return; } try { const result = calculate(inputVal); $('#result').text(`计算结果:${result}`); } catch (err) { $('#result').text('表达式解析出错'); } });
思路2:逆波兰表达式(后缀表达式)转换
这是编译领域常用的方法,先把人类习惯的中缀表达式(比如(5*5)+4)转换成计算机更容易计算的后缀表达式(5 5 * 4 +),再用栈来计算结果。
具体步骤:
- 拆分Token:把表达式拆成数字、运算符、括号的数组
- 中缀转后缀:根据运算符优先级(括号>乘除>加减),把中缀表达式转换成后缀表达式
- 栈计算后缀表达式:遇到数字压栈,遇到运算符弹出两个数计算,结果压栈,最后栈顶就是结果
代码示例:
// 运算符优先级配置 const opPrecedence = { '+': 1, '-': 1, '*': 2, '/': 2 }; // 中缀表达式转后缀表达式 function infixToPostfix(expr) { const stack = []; const postfix = []; // 拆分token:匹配数字(含负数)、运算符、括号 const tokens = expr.match(/(-?\d+(\.\d+)?|[+\*/()])/g); for (const token of tokens) { if (!isNaN(parseFloat(token))) { // 数字直接加入后缀列表 postfix.push(parseFloat(token)); } else if (token === '(') { // 左括号压栈 stack.push(token); } else if (token === ')') { // 右括号:弹出栈中运算符直到左括号 while (stack.length && stack[stack.length-1] !== '(') { postfix.push(stack.pop()); } stack.pop(); // 弹出左括号(不加入后缀) } else { // 运算符:弹出栈中优先级>=当前的运算符 while (stack.length && stack[stack.length-1] !== '(' && opPrecedence[stack[stack.length-1]] >= opPrecedence[token]) { postfix.push(stack.pop()); } stack.push(token); } } // 弹出栈中剩余运算符 while (stack.length) { postfix.push(stack.pop()); } return postfix; } // 计算后缀表达式 function calculatePostfix(postfix) { const stack = []; for (const token of postfix) { if (typeof token === 'number') { stack.push(token); } else { // 注意顺序:后弹出的是第一个操作数 const num2 = stack.pop(); const num1 = stack.pop(); let result; switch(token) { case '+': result = num1 + num2; break; case '-': result = num1 - num2; break; case '*': result = num1 * num2; break; case '/': result = num1 / num2; break; } stack.push(result); } } return stack[0]; } // jQuery调用示例 $('#calc-btn').click(function() { const inputVal = $('#calc-input').val().replace(/\s+/g, ''); // 输入验证 const openCount = (inputVal.match(/\(/g) || []).length; const closeCount = (inputVal.match(/\)/g) || []).length; if (!inputVal || openCount !== closeCount || !/^[\d+\-*/().]+$/.test(inputVal)) { $('#result').text('请输入合法的表达式'); return; } try { const postfix = infixToPostfix(inputVal); const result = calculatePostfix(postfix); $('#result').text(`计算结果:${result}`); } catch (err) { $('#result').text('表达式解析出错'); } });
额外注意事项
- 输入验证:一定要加验证逻辑,比如检查括号是否配对、有没有非法字符,避免解析时出错
- 负数处理:上面的代码已经考虑了
-5这种负数情况,但如果是3*-2这种表达式,可能需要调整Token拆分逻辑,或者预处理表达式把*-换成*-1*之类的 - 浮点数精度:JavaScript浮点数计算有精度问题(比如
0.1+0.2=0.30000000000000004),如果需要高精度,可以用toFixed(n)限制小数位数,或者引入专门的数学库
内容的提问来源于stack exchange,提问作者ejafe
相关产品推荐
相关产品推荐

