x-spreadsheet公式引擎详解:Expression.js与Formula.js实现
在数据处理场景中,你是否常因复杂计算逻辑而头疼?x-spreadsheet作为一款轻量级电子表格库,其公式引擎通过src/algorithm/expression.js与src/core/formula.js两大核心模块,实现了从表达式解析到函数计算的完整流程。本文将带你深入了解这两个模块的工作原理,掌握自定义公式的扩展方法,并通过实际案例展示如何解决常见计算难题。
公式引擎架构概览
x-spreadsheet的公式处理采用分层设计,主要包含表达式解析层与函数计算层:
- 表达式解析层:由src/algorithm/expression.js实现,负责将类似
9+(3-1)*3+10/2的中缀表达式转换为计算机可执行的后缀表达式(逆波兰式) - 函数计算层:由src/core/formula.js实现,提供SUM、AVERAGE等内置函数,并支持自定义函数扩展
Expression.js:表达式解析核心
中缀转后缀算法实现
src/algorithm/expression.js中的infix2suffix函数是公式解析的关键,其核心逻辑基于栈数据结构:
const infix2suffix = (src) => {
const operatorStack = [];
const stack = [];
for (let i = 0; i < src.length; i += 1) {
const c = src.charAt(i);
if (c !== ' ') {
if (c >= '0' && c <= '9') {
stack.push(c); // 数字直接入结果栈
} else if (c === ')') {
// 遇到右括号弹出运算符直到左括号
let c1 = operatorStack.pop();
while (c1 !== '(') {
stack.push(c1);
c1 = operatorStack.pop();
}
} else {
// 运算符优先级处理:*/ 高于 +-
if (operatorStack.length > 0 && (c === '+' || c === '-')) {
const last = operatorStack[operatorStack.length - 1];
if (last === '*' || last === '/') {
while (operatorStack.length > 0) {
stack.push(operatorStack.pop());
}
}
}
operatorStack.push(c);
}
}
}
// 弹出剩余运算符
while (operatorStack.length > 0) {
stack.push(operatorStack.pop());
}
return stack;
};
该算法通过两个栈(运算符栈与结果栈)实现转换,遵循"先乘除后加减,括号优先"的运算规则。例如将9+(3-1)*3+10/2转换为9 3 1 - 3 * + 10 2 / +的后缀表达式,便于后续计算。
Formula.js:函数计算引擎
内置函数体系
src/core/formula.js定义了基础公式集合baseFormulas,包含SUM、AVERAGE等常用函数:
const baseFormulas = [
{
key: 'SUM',
title: tf('formula.sum'),
render: ary => ary.reduce((a, b) => numberCalc('+', a, b), 0),
},
{
key: 'AVERAGE',
title: tf('formula.average'),
render: ary => ary.reduce((a, b) => Number(a) + Number(b), 0) / ary.length,
},
{
key: 'MAX',
title: tf('formula.max'),
render: ary => Math.max(...ary.map(v => Number(v))),
},
// 更多函数定义...
];
每个公式对象包含三个核心属性:
key:函数标识符(如'SUM')title:本地化名称(通过tf函数从src/locale/zh-cn.js获取)render:计算逻辑实现,接收参数数组并返回计算结果
函数调用流程
当用户输入=SUM(A1:B3)时,公式引擎执行以下步骤:
- 解析A1:B3获取目标单元格数据集合
- 调用SUM函数的
render方法,传入数据数组 - 通过
numberCalc工具函数处理数值累加(来自src/core/helper.js) - 返回计算结果并更新单元格显示
实战应用:自定义公式扩展
扩展步骤
- 定义新公式对象,实现
render方法 - 将自定义公式添加到
baseFormulas数组 - 注册本地化名称(在docs/locale/zh-cn.js中添加对应翻译)
示例:实现COUNTIF函数
// 自定义COUNTIF函数示例
{
key: 'COUNTIF',
title: tf('formula.countif'),
render: (range, condition) => {
return range.filter(cell => {
// 实现条件判断逻辑
const value = Number(cell);
return eval(`${value}${condition}`); // 简化示例,实际应用需优化
}).length;
}
}
注意事项与性能优化
- 数据类型处理:使用
numberCalc而非直接运算,避免精度问题 - 错误处理:添加参数校验与异常捕获(当前实现中需完善)
- 性能优化:对于大型数据集,考虑实现计算结果缓存机制
- 兼容性:公式语法设计需兼容主流电子表格软件习惯
总结与展望
x-spreadsheet的公式引擎通过模块化设计,实现了轻量级与功能性的平衡。核心模块src/algorithm/expression.js与src/core/formula.js分别解决了表达式解析与函数计算两大关键问题。未来可通过扩展运算符支持、优化解析算法、增强错误处理等方向进一步提升引擎能力。
官方文档:docs/index.html 核心算法实现:src/algorithm/ 公式定义源码:src/core/formula.js
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考




