在前端业务开发中,经常会遇到多级嵌套的树形数据结构,比如部门层级、商品分类、订单明细等场景,这类结构通常需要按层级统计对应的金额总和。递归是处理这类嵌套结构最直观高效的方式,通过递归可以逐层遍历所有节点,完成金额的累加计算。

典型的多级嵌套数据结构
首先我们来看一个常见的多级嵌套结构示例,每个节点包含自身金额和子节点列表:
// 多级嵌套的金额数据结构
const data = [
{
id: 1,
name: '部门A',
amount: 1000,
children: [
{
id: 11,
name: '部门A1',
amount: 500,
children: [
{ id: 111, name: '部门A11', amount: 200, children: [] },
{ id: 112, name: '部门A12', amount: 300, children: [] }
]
},
{
id: 12,
name: '部门A2',
amount: 400,
children: []
}
]
},
{
id: 2,
name: '部门B',
amount: 800,
children: [
{ id: 21, name: '部门B1', amount: 300, children: [] }
]
}
];
递归实现层级金额汇总的核心思路
递归处理嵌套结构的核心逻辑是:先处理当前节点的自身金额,再遍历当前节点的所有子节点,对每个子节点重复执行相同的处理逻辑,直到子节点为空为止。对于层级汇总的需求,我们需要让每个节点的汇总金额等于自身金额加上所有子节点的汇总金额。
基础递归实现
下面是实现多级嵌套结构按层级汇总金额的递归函数:
/**
* 递归计算每个节点的层级汇总金额
* @param {Array} nodes 嵌套结构节点数组
* @returns {Array} 包含层级汇总金额的节点数组
*/
function calculateLevelAmount(nodes) {
// 遍历当前层级的每个节点
return nodes.map(node => {
// 初始化当前节点的汇总金额为自身金额
let totalAmount = node.amount;
// 如果当前节点有子节点,递归处理子节点
if (node.children && node.children.length > 0) {
// 递归处理子节点,得到带汇总金额的子节点数组
const childNodes = calculateLevelAmount(node.children);
// 累加所有子节点的汇总金额
childNodes.forEach(child => {
totalAmount += child.totalAmount;
});
// 将处理后的子节点赋值回当前节点,同时添加汇总金额属性
return {
...node,
children: childNodes,
totalAmount: totalAmount
};
}
// 没有子节点的节点,汇总金额就是自身金额
return {
...node,
totalAmount: totalAmount
};
});
}
// 调用函数处理数据
const result = calculateLevelAmount(data);
console.log(JSON.stringify(result, null, 2));
代码逻辑解析
- 函数接收一个嵌套结构的节点数组作为参数,使用
map方法遍历每个节点 - 每个节点的汇总金额初始值为自身的
amount属性 - 判断节点是否存在
children属性且子节点数组长度大于0,如果存在则递归调用函数处理子节点 - 递归返回的子节点已经包含各自的汇总金额,将子节点的汇总金额累加到当前节点的
totalAmount中 - 最终返回包含
totalAmount属性的新节点结构,不修改原始数据结构
边界情况处理
实际业务中可能会遇到一些特殊的数据情况,需要对递归函数做对应的兼容处理:
空数组输入处理
当传入的节点数组为空时,直接返回空数组即可:
function calculateLevelAmount(nodes) {
// 处理空输入的情况
if (!Array.isArray(nodes) || nodes.length === 0) {
return [];
}
return nodes.map(node => {
let totalAmount = node.amount || 0; // 兼容amount属性不存在的情况
if (node.children && Array.isArray(node.children) && node.children.length > 0) {
const childNodes = calculateLevelAmount(node.children);
childNodes.forEach(child => {
totalAmount += child.totalAmount;
});
return {
...node,
children: childNodes,
totalAmount: totalAmount
};
}
return {
...node,
totalAmount: totalAmount
};
});
}
非标准嵌套结构处理
如果部分节点的children属性不是数组,需要做类型校验,避免递归报错:
function calculateLevelAmount(nodes) {
if (!Array.isArray(nodes) || nodes.length === 0) {
return [];
}
return nodes.map(node => {
// 兼容amount为undefined、null或者非数字的情况
const selfAmount = typeof node.amount === 'number' ? node.amount : 0;
let totalAmount = selfAmount;
// 校验children是否为有效数组
const children = Array.isArray(node.children) ? node.children : [];
if (children.length > 0) {
const childNodes = calculateLevelAmount(children);
childNodes.forEach(child => {
totalAmount += child.totalAmount;
});
return {
...node,
children: childNodes,
totalAmount: totalAmount
};
}
return {
...node,
totalAmount: totalAmount
};
});
}
递归方法的注意事项
- 递归深度限制:如果嵌套层级非常深,比如超过1000层,可能会触发JavaScript的递归栈溢出错误,这种情况下可以考虑使用迭代加栈的方式替代递归
- 数据不可变:上述实现中通过扩展运算符返回新对象,不会修改原始数据结构,符合函数式编程的最佳实践,避免副作用
- 金额精度问题:如果金额涉及小数计算,建议使用整数计算或者专门的精度处理库,避免浮点数计算误差
迭代替代方案(可选)
如果遇到递归栈溢出的问题,可以使用栈模拟递归过程实现相同的功能:
function calculateLevelAmountByIterate(nodes) {
if (!Array.isArray(nodes) || nodes.length === 0) {
return [];
}
// 使用栈存储待处理的节点,每个元素包含节点本身和父节点引用
const stack = nodes.map(node => ({ node, parent: null }));
const result = [];
// 先遍历所有节点,收集所有节点的引用
const nodeMap = new Map();
while (stack.length > 0) {
const { node, parent } = stack.pop();
const selfAmount = typeof node.amount === 'number' ? node.amount : 0;
const newNode = { ...node, totalAmount: selfAmount };
nodeMap.set(node.id, newNode);
// 如果有父节点,把当前节点加入父节点的children
if (parent) {
if (!parent.children) parent.children = [];
parent.children.push(newNode);
} else {
result.push(newNode);
}
// 子节点入栈
const children = Array.isArray(node.children) ? node.children : [];
children.forEach(child => {
stack.push({ node: child, parent: newNode });
});
}
// 第二次遍历,从最底层节点向上累加金额
// 这里可以结合后序遍历的逻辑,先处理子节点再处理父节点
// 实际实现可以根据具体数据结构调整,核心是保证子节点先完成汇总再累加给父节点
return result;
}
通过上述递归实现,我们可以快速完成多级嵌套结构的层级金额汇总需求,处理逻辑清晰,代码可维护性高,能够覆盖大部分常规的业务场景。
JavaScript递归嵌套结构金额汇总修改时间:2026-07-02 05:42:41