最近接到了一个新需求,要求实现一个动态的录入表单,这个表单绝大多数都是数字录入,并且其中的某些字段是有关联关系的。例如三个字段 field1
、field2
、field3
,它们的关系可能为:field3 = field1 + field2
。到时候后端会提供给你这个表达式,前端来自动计算。
你可能会想,没啥问题,监听 form 值变更,依赖字段变化了就把这个表达式里的字段名字符串替换成实际值,最后 eval
一下不就好了。我一开始也确实是这么做的,但是 js 的精度问题给了我一脚:
如果两个字段的值分别为 0.1 和 0.2,最后计算的结果将会为 0.30000000000000004。这在这种数字表单录入里是不能接受的,单纯的乘 100 最后除 100 也不靠谱,加上 eval 本身的不安全性,所以路还是要一步步的走。
现在回看上面的描述,我们可以把问题整理成下面这道题(完整实现见文末):
/**
* 实现一个函数,接收表达式模板和字段值,计算最终结果
*/
const templateCalc = (template, values) => {
// ...
}
templateCalc('(val1 + val2) / val3', { val1: 1, val2: 2, val3: 2 });
// 1.5
templateCalc('((val1 + val2) - val3 * val4) / val3', { val1: 1, val2: 2, val3: 2, val4: 10 });
// -8.5
templateCalc('val1 - val2', { val1: 0.3, val2: 0.2 });
// 0.1
templateCalc('(val1 + val2) / 10000', { val1: 100, val2: 5 });
// 0.0105
复制代码
在精度问题上,我选择了 big.js – npm (npmjs.com) 来处理。处理的整体思路如下:
- 把模板中的字段名和操作符拆开,即将字符串解析为 token 数组
- 把 token 数组处理成逆波兰表达式
- 计算逆波兰表达式时将字段名替换为实际值并引入 big.js 计算
想法不错,赞一个