如何在没有 eval 的 javascript 中编写计算器

Posted

技术标签:

【中文标题】如何在没有 eval 的 javascript 中编写计算器【英文标题】:How to code a calculator in javascript without eval 【发布时间】:2015-11-24 08:46:26 【问题描述】:

所以,我到处搜索,但找不到答案。我已经尝试了大约 3 次,并通过基本上将输入作为字符串存储在数组中,解析数字,然后打开运算符来计算整数,从而得到了一个基本的,但我有一个真的很难弄清楚链接逻辑。有没有人有什么建议?甚至可能只是伪代码?我真的不想使用 eval。非常感谢

【问题讨论】:

你打算如何使用计算器?你想要一个主要的计算器函数并将表达式作为字符串传递给它吗?像这样:calculate("1 + 2 / 9") 我会采取任何有效的方法,在这一点上。这不是我的意图,因为我认为像这样在最后评估所有操作员可能会更加痛苦,因为我必须考虑操作顺序,但如果你知道一种方法,我如果你愿意分享,我会很高兴。我打算做一些事情,如果用户输入1+3*5-10%,脚本过程将评估1+3,然后存储它,然后是5*5,存储它,然后是25-10,存储它,然后输出0.15 . 是的。不过,必须有某种逻辑才能知道在哪里拆分表达式。​​ *** 上的一个类似问题,但有更多答案,可在here 获得。 【参考方案1】:

对于只有 5 个运算符(^、*、/、+、-)且没有括号的简单计算器,您可以这样做。首先,将字符串转换为数字和运算符的数组很方便。然后,我们遍历数组,按优先顺序查找每个运算符,并将运算符应用于它之前和之后的数字。

function tokenize(s) 
    // --- Parse a calculation string into an array of numbers and operators
    const r = [];
    let token = '';
    for (const character of s) 
        if ('^*/+-'.indexOf(character) > -1) 
            if (token === '' && character === '-') 
                token = '-';
             else 
                r.push(parseFloat(token), character);
                token = '';
            
         else 
            token += character;
        
    
    if (token !== '') 
        r.push(parseFloat(token));
    
    return r;


function calculate(tokens) 
    // --- Perform a calculation expressed as an array of operators and numbers
    const operatorPrecedence = ['^': (a, b) => Math.pow(a, b),
               '*': (a, b) => a * b, '/': (a, b) => a / b,
               '+': (a, b) => a + b, '-': (a, b) => a - b];
    let operator;
    for (const operators of operatorPrecedence) 
        const newTokens = [];
        for (const token of tokens) 
            if (token in operators) 
                operator = operators[token];
             else if (operator) 
                newTokens[newTokens.length - 1] = 
                    operator(newTokens[newTokens.length - 1], token);
                operator = null;
             else 
                newTokens.push(token);
            
        
        tokens = newTokens;
    
    if (tokens.length > 1) 
        console.log('Error: unable to resolve calculation');
        return tokens;
     else 
        return tokens[0];
    

const calculateButton = document.getElementById('calculate');
const userInput = document.getElementById('userInput');
const result = document.getElementById('result');
calculateButton.addEventListener('click', function() 
    result.innerhtml = "The answer is " + calculate(tokenize(userInput.value));
);
<input type="text" id="userInput" />
<input type="button" value="Calculate" id="calculate" />
<div id="result"></div>

(jsfiddle)。要允许括号,您可以告诉calculate 函数在开始查找任何其他运算符之前检查括号,然后在每组括号内的表达式上递归调用自身。解析功能也可以改进,例如删除任何空白并处理错误。

【讨论】:

我注意到一个错误:当你尝试计算这样的东西时:10.01 - 11 你会得到0.9900000000000002这是错误的。 @TomSki 这是 JS 和大多数语言 ***.com/questions/1458633/… 中十进制计算的普遍问题。它可以使用像decimal.js github.com/MikeMcl/decimal.js这样的库来处理,就像这个计算器的更新版本jsfiddle.net/10wn7bun/2 谢谢,最近几天我一直在寻找解决这个问题的方法,但我找不到任何简单的方法。似乎 decimal.js 对我来说是最好的选择。 @Ghos3t |这是一个很晚的回应,但数学家通常使用 BIDMAS。括号、索引、除法、乘法、加法、减法,具体按此顺序。它不是从左到右或从右到左,而是使用 BIDMAS。 @Tigerrrrr Ghos3t 的评论是对早期版本答案中的错误的回应,该版本已修复。运算确实按照 (1) 指数 (2) 乘法/除法 (3) 加法/减法的顺序从左到右进行。例如 40-5+6 = (40-5)+6 而不是 40-(5+6) - 首先完成最左边的操作。加法不一定在减法之前完成,BIDMAS 等在这方面可能会令人困惑。 en.m.wikipedia.org/wiki/Order_of_operations

以上是关于如何在没有 eval 的 javascript 中编写计算器的主要内容,如果未能解决你的问题,请参考以下文章

Javascript:从字符串调用以匿名函数编写的函数,函数名称没有eval?

JavaScript中,为什么eval和with会有性能问题?

JS中eval()解析和为什么不要使用eval

是否仍然值得在移动 JavaScript 上使用 eval 来提高性能?

不使用 Eval 动态引用 Javascript 数组名称?

XSS小记:Javascript 中 eval 与 fetch组合在攻击场景中的使用