我在网页(150+)中有很多字段需要运行方程式才能生成结果。
我目前存储这样的等式:
<input name="F7" type="text" class="numeric" data-formula="([C7]-[D7])/[E7]" readonly />
当输入模糊时,我使用jQuery选择器迭代所有带有data-formula
属性的输入,取公式,并使用正则表达式替换指针(等式中的[C7]
)用适当的价值观。
之后,我eval()
等式得到一个结果,并把它放在正确的输入中。这很好用,但非常慢,导致网页挂起几秒钟,如果每次输入模糊时都会发生这种情况很糟糕。
有没有办法在不使用eval()
的情况下评估等式,例如“(1-2)/ 4”?这些方程也可能有函数,例如平方根(这使得eval()
很好,因为我可以将Math.sqrt()
放在公式中),并且数字可能是小数。
注意:此应用程序必须在IE7和8上运行,所以我不相信我可以使用Webworkers或类似的东西。我还考虑在点击“保存”按钮后仅运行此代码,但我希望UI尽可能更新直播
答案 0 :(得分:4)
有没有办法在不使用
eval()
的情况下评估等式,例如“(1-2)/ 4”?
好吧,您可以对表达式进行标记,并编写自己的评估程序,模仿eval
的作用。虽然这在限制副作用方面可能有用(因为eval
是一个非常大的锤子),但是 极 不太可能比{更好{1}}确实。
但是,您可以执行的操作是缓存评估所有其他输入的结果,以便您只评估实际模糊的输入。那应该是非常有效的。
例如,假设您有此全局对象:
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();
});
使用doTheEvaluation
中的值,而不是每次重新计算所有值。
如果values
可能引用其他字段,您可以对其进行递归评估 - 但不评估输入的所有。
答案 1 :(得分:4)
我只知道两种选择,一种是使用动态写入页面的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();
}
但我认为你找不到与eval
产生类似效果的东西:http://jsperf.com/alternative-evaluation
eval
的性能因浏览器和平台而异,您是否考虑过特定的浏览器/平台组合?改进的浏览器中较新的JavaScript引擎将提供优化的eval
:
这只是对一些UA的一组有限测试,但它应该让您了解它在不同环境中的表现。
答案 2 :(得分: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
答案 3 :(得分:1)
我会修改你的代码只执行一个eval。
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];
答案 4 :(得分:0)
如果您有可靠的互联网连接,您可以连接到谷歌并使用他们的服务来评估表达式。谷歌有一个非常强大的服务器,你所要做的就是发送一个请求,队列就是等式并检索它。当然,这可能会更慢或更快,具体取决于互联网速度/浏览器速度。
或者,您可以编写自己的等式评估程序。这非常困难,并且可能不会比eval更有效。您还必须经历PEMDAS订单的巨大麻烦。
我建议您可以将这些方程式合并为一个字符串,并同时评估所有方法,并一次性检索所有结果。
答案 5 :(得分:0)
您可以使用new Function
来评估您的表达式
答案 6 :(得分:0)
我确实知道这个答案已经晚了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()替换变量,以提供类似于代码段中示例用法的字符串。