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

不使用eval()实现JavaScript计算器是否可行?

不用eval/Function实现JavaScript计算器的函数式方案

不用eval()或Function()构造函数是更安全、更符合工程规范的做法,用函数式编程思路可以实现一个健壮的计算器,核心是通过词法分析→表达式转换→结果计算三个纯函数步骤完成,全程无副作用,逻辑清晰。

1. 词法分析:将表达式拆分为Token

首先需要把输入的表达式字符串(比如"3+4*(2-1)")拆分成有意义的单元(Token),包括数字、运算符、括号。这个过程用纯函数实现:

function tokenize(expression) {
  // 匹配数字(含小数)、运算符、括号的正则
  const regex = /\d+\.?\d*|[-+*/()]/g;
  return expression.match(regex) || [];
}

这个函数接收字符串,返回Token数组,比如输入"3.5+2*(5-1)"会得到["3.5", "+", "2", "*", "(", "5", "-", "1", ")"]。

2. 转换为后缀表达式(Shunting-yard算法)

中缀表达式(日常写的a+b*c)的运算符优先级和括号处理起来复杂,转成后缀表达式(ab*+)后可以按顺序直接计算。用纯函数实现转换逻辑,依赖运算符优先级映射:

// 运算符优先级,数值越高优先级越高
const operatorPrecedence = {
  '+': 1,
  '-': 1,
  '*': 2,
  '/': 2
};

function infixToPostfix(tokens) {
  const output = [];
  const operatorStack = [];

  for (const token of tokens) {
    // 数字直接加入输出队列
    if (!isNaN(parseFloat(token))) {
      output.push(parseFloat(token));
    } 
    // 左括号压入栈
    else if (token === '(') {
      operatorStack.push(token);
    } 
    // 右括号:弹出栈内运算符直到遇到左括号
    else if (token === ')') {
      let top = operatorStack.pop();
      while (top !== '(') {
        output.push(top);
        top = operatorStack.pop();
      }
    } 
    // 运算符:按优先级处理栈内元素
    else {
      while (operatorStack.length > 0 && 
             operatorStack[operatorStack.length - 1] !== '(' &&
             operatorPrecedence[operatorStack[operatorStack.length - 1]] >= operatorPrecedence[token]) {
        output.push(operatorStack.pop());
      }
      operatorStack.push(token);
    }
  }

  // 弹出栈内剩余运算符
  while (operatorStack.length > 0) {
    output.push(operatorStack.pop());
  }

  return output;
}

这个函数接收Token数组,返回后缀表达式数组,比如输入之前的Token数组会得到[3.5, 2, 5, 1, '-', '*', '+']。

3. 计算后缀表达式

最后遍历后缀表达式,用栈存储中间结果,遇到运算符就取出栈顶两个数计算,结果压回栈。这里用函数映射来处理运算,完全符合函数式风格:

// 运算符对应的运算函数
const operators = {
  '+': (a, b) => a + b,
  '-': (a, b) => a - b,
  '*': (a, b) => a * b,
  '/': (a, b) => {
    if (b === 0) throw new Error('除数不能为0');
    return a / b;
  }
};

function evaluatePostfix(postfix) {
  const stack = [];

  for (const token of postfix) {
    if (typeof token === 'number') {
      stack.push(token);
    } else {
      const b = stack.pop();
      const a = stack.pop();
      stack.push(operators[token](a, b));
    }
  }

  return stack.pop();
}

整合完整计算器

把三个纯函数组合起来,得到最终的计算器函数:

function calculate(expression) {
  const tokens = tokenize(expression);
  const postfix = infixToPostfix(tokens);
  return evaluatePostfix(postfix);
}

// 测试示例
console.log(calculate("3+4*(2-1)")); // 7
console.log(calculate("3.5+2*(5-1)")); // 11.5
console.log(calculate("10/(5-3)")); // 5

函数式编程特点说明

  • 所有核心函数都是纯函数:相同输入必定返回相同输出,不修改外部状态,只依赖参数传递。
  • 用函数映射替代条件判断:operators对象把运算符映射到对应的运算函数,符合函数式的"用函数抽象逻辑"思路。
  • 无副作用:整个计算过程不会修改外部变量,所有状态都在函数内部的栈或数组中处理。

内容的提问来源于stack exchange,提问作者Mike Sphere

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.31 11:25:43