6

我在网页中有一堆字段(150+),需要在它们上运行方程式才能产生结果。

我目前存储这样的方程:

<input name="F7" type="text" class="numeric" data-formula="([C7]-[D7])/[E7]" readonly />

当输入模糊时,我使用 jQuery 选择器遍历具有data-formula属性的所有输入,获取公式,并使用正则表达式将指针([C7]等式中的)替换为适当的值。

之后,我eval()用方程得到一个结果,并将其放入正确的输入中。这很好用,但速度慢,导致网页挂起几秒钟,如果每次输入模糊时都会发生这种情况,这很糟糕。

有没有一种方法可以在不使用 的情况下评估方程,例如“(1-2)/4” eval()?这些方程也可能有函数,比如平方根(这eval()很好,因为我可以把Math.sqrt()公式放进去),数字可能是小数。

注意:这个应用程序必须在 IE7 和 8 上运行,所以我不相信我可以使用 Webworkers 或类似的东西。我还考虑过只在点击“保存”按钮后运行此代码,但如果可能的话,我希望 UI 能够实时更新

4

7 回答 7

5

我只知道两种选择,一种是使用script动态写入页面的元素,例如:

function evaluate(formula)
{
  var script = document.createElement("script");
  script.type = "text/javascript";
  script.text = "window.__lr = " + formula + ";";
  document.body.appendChild(script);
  document.body.removeChild(script);

  var r = window.__lr;

  return r;
}

另一种是使用new Function(...)

function evaluate3(formula)
{
  var func = new Function("return " + formula);
  return func();
}

但我认为您不会找到与以下性能相似的东西:http eval: //jsperf.com/alternative-evaluation

性能eval因浏览器和平台而异,您是否考虑过特定​​的浏览器/平台组合?改进的浏览器中更新的 javascript 引擎将提供优化的eval

测试结果

这只是对少数 UA 的一组有限测试,但它应该让您了解它在不同环境中的表现。

于 2012-07-10T22:17:45.087 回答
4

有没有一种方法可以在不使用 的情况下评估方程,例如“(1-2)/4” eval()

好吧,您可以对表达式进行标记并编写自己的评估器来模仿所做的事情eval。但是,虽然这在限制副作用方面可能有用(因为eval它是一个非常大的锤子),但它的性能不可能比eval它更好。

但是,您可以做的是缓存评估所有其他输入的结果,以便您只评估实际模糊的输入。这确实应该是相当有效的。

例如,假设您有这个全局对象:

var values = {
   A7: /* initial value for A7 */,
   B7: /* initial value for B7 */,
   C7: /* initial value for C7 */,
   D7: /* initial value for D7 */,
   E7: /* initial value for E7 */,
   F7: /* initial value for F7 */,
   /* etc */
};

...然后将此blur处理程序附加到所有输入:

$("input").blur(function() {
    values[this.id] = this.value; // Or parseInt(this.value, 10), or parseFloat(this.value), etc.
    doTheEvaluation();
});

... wheredoTheEvaluation使用了这些值,values而不是每次都重新计算它们。

如果this.value可能引用其他字段,您可以对其进行递归评估 - 但无需评估所有输入。

于 2012-07-10T21:48:00.543 回答
2

我确实意识到这个答案已经晚了 8 年,但我想我会添加自己的贡献,因为这个问题出现在我正在从事的项目中。就我而言,我使用的是 Nodejs,但这个解决方案也应该适用于浏览器。

let parens = /\(([0-9+\-*/\^ .]+)\)/             // Regex for identifying parenthetical expressions
let exp = /(\d+(?:\.\d+)?) ?\^ ?(\d+(?:\.\d+)?)/ // Regex for identifying exponentials (x ^ y)
let mul = /(\d+(?:\.\d+)?) ?\* ?(\d+(?:\.\d+)?)/ // Regex for identifying multiplication (x * y)
let div = /(\d+(?:\.\d+)?) ?\/ ?(\d+(?:\.\d+)?)/ // Regex for identifying division (x / y)
let add = /(\d+(?:\.\d+)?) ?\+ ?(\d+(?:\.\d+)?)/ // Regex for identifying addition (x + y)
let sub = /(\d+(?:\.\d+)?) ?- ?(\d+(?:\.\d+)?)/  // Regex for identifying subtraction (x - y)

/**
 * Evaluates a numerical expression as a string and returns a Number
 * Follows standard PEMDAS operation ordering
 * @param {String} expr Numerical expression input
 * @returns {Number} Result of expression
 */
function evaluate(expr)
{
    if(isNaN(Number(expr)))
    {
        if(parens.test(expr))
        {
            let newExpr = expr.replace(parens, function(match, subExpr) {
                return evaluate(subExpr);
            });
            return evaluate(newExpr);
        }
        else if(exp.test(expr))
        {
            let newExpr = expr.replace(exp, function(match, base, pow) {
                return Math.pow(Number(base), Number(pow));
            });
            return evaluate(newExpr);
        }
        else if(mul.test(expr))
        {
            let newExpr = expr.replace(mul, function(match, a, b) {
                return Number(a) * Number(b);
            });
            return evaluate(newExpr);
        }
        else if(div.test(expr))
        {
            let newExpr = expr.replace(div, function(match, a, b) {
                if(b != 0)
                    return Number(a) / Number(b);
                else
                    throw new Error('Division by zero');
            });
            return evaluate(newExpr);
        }
        else if(add.test(expr))
        {
            let newExpr = expr.replace(add, function(match, a, b) {
                return Number(a) + Number(b);
            });
            return evaluate(newExpr);
        }
        else if(sub.test(expr))
        {
            let newExpr = expr.replace(sub, function(match, a, b) {
                return Number(a) - Number(b);
            });
            return evaluate(newExpr);
        }
        else
        {
            return expr;
        }
    }
    return Number(expr);
}
// Example usage
//console.log(evaluate("2 + 4*(30/5) - 34 + 45/2"));

在原始帖子中,可以使用 String.replace() 替换变量以提供类似于片段中的示例用法的字符串。

于 2020-07-26T20:48:24.733 回答
1

验证:我会编写一个强大的正则表达式来验证输入,然后使用eval它来评估它是否安全。

评估:关于评估的速度:如果这是一个大问题,您可以将所有方程式排队(将其存储在一个数组中),并一次评估它们:

var equations = ['1+1', '2+2', '...'];   //<-- Input from your fields
var toBeEvald = '[' + equations.join(',') + '];';
var results = eval(toBeEvald);
// result[0] = 2
// result[1] = 4, etc
于 2012-07-10T21:48:41.377 回答
1

我会修改您的代码以仅执行一次评估。

var expressions = []
// for each field
// expressions.push("id:" + parsedExpression);
var members = expressions.join(",");
var resultObj = eval("({" + members + "})");
// for each field 
document.getElementById(id).value = resultObj[id];
于 2012-07-10T21:51:44.640 回答
0

如果您有可靠的互联网连接,您可以连接到 google 并使用他们的服务来评估表达式。谷歌有一个非常强大的服务器,你所要做的就是发送一个请求,队列是方程并检索它。当然,这可能会更慢或更快,具体取决于互联网速度/浏览器速度。

或者,您可以编写自己的方程计算器。这非常困难,并且可能不会比 eval 更有效。您还必须经历 PEMDAS 订单的巨大麻烦。

我建议您可以将方程式合并为一个字符串,然后一次全部评估,然后一次检索所有结果。

于 2012-07-10T21:54:40.147 回答
0

您可以使用new Function来评估您的表达式

于 2015-11-18T08:29:54.983 回答