You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于HTML+JQuery开发计算器:无eval()实现表达式计算方案咨询

嘿,我来给你分享几个不用eval()就能实现带括号表达式计算的靠谱思路,刚好适配你的HTML+jQuery计算器需求~

思路1:递归解析+优先级分步计算

这个思路的核心是模拟人类计算表达式的逻辑:先算括号里的,再算乘除,最后算加减,用递归处理嵌套括号。

具体步骤:

  1. 预处理输入:用jQuery获取输入框的值,先去掉空格,统一运算符格式(比如把中文乘除换成*//)
  2. 递归处理括号:找到最内层的括号,计算里面的子表达式,把结果替换原括号部分,重复这个过程直到整个表达式没有括号
  3. 分步处理乘除/加减:先遍历表达式计算所有乘除,再处理剩下的加减

代码示例:

// 核心计算函数
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 +),再用栈来计算结果。

具体步骤:

  1. 拆分Token:把表达式拆成数字、运算符、括号的数组
  2. 中缀转后缀:根据运算符优先级(括号>乘除>加减),把中缀表达式转换成后缀表达式
  3. 栈计算后缀表达式:遇到数字压栈,遇到运算符弹出两个数计算,结果压栈,最后栈顶就是结果

代码示例:

// 运算符优先级配置
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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.20 07:18:13