递归处理树形结构的核心是先操作当前节点再递归子节点;包括基础遍历、查找节点、映射生成新树三类应用;需注意防止无限递归、空值防御、性能优化及不可变性。

递归处理树形结构数据在 JavaScript 中很常见,比如菜单渲染、权限校验、组织架构遍历等。核心思路是:对当前节点做操作,再对它的每个子节点递归调用同一函数。
基础递归遍历(深度优先)
适用于需要访问每一个节点的场景,如收集所有节点 ID、查找目标节点、打平为一维数组:
function traverseTree(nodes, callback) {
if (!Array.isArray(nodes)) return;
for (const node of nodes) {
callback(node); // 先处理当前节点
// 若存在 children,递归处理
if (Array.isArray(node.children)) {
traverseTree(node.children, callback);
}
}
}
// 使用示例:打印所有节点名称
traverseTree(treeData, node => console.log(node.name));
递归查找指定节点
返回匹配的第一个节点(可按 id、name 或其他字段),支持提前终止:
function findNodeById(nodes, targetId) {
if (!Array.isArray(nodes)) return null;
for (const node of nodes) {
if (node.id === targetId) return node; // 找到即返回
if (Array.isArray(node.children)) {
const found = findNodeById(node.children, targetId);
if (found) return found; // 子树中找到就立即返回
}
}
return null;
}
递归生成新树(映射/过滤/转换)
常用于权限控制(过滤不可见节点)、添加计算字段、格式标准化等。注意保持原结构,返回新对象避免副作用:
立即学习“Java免费学习笔记(深入)”;
function mapTree(nodes, mapper) {
if (!Array.isArray(nodes)) return [];
return nodes.map(node => {
// 对当前节点应用映射函数(深拷贝 + 修改)
const newNode = { ...node };
mapper(newNode);
// 递归处理 children,并赋值回去
if (Array.isArray(node.children)) {
newNode.children = mapTree(node.children, mapper);
}
return newNode;
});
}
// 使用示例:为每个节点添加 hasChildren 标志
const newTree = mapTree(treeData, node => {
node.hasChildren = Array.isArray(node.children) && node.children.length > 0;
});
安全与边界处理要点
实际项目中需防范常见陷阱:
- 防止无限递归:确保 children 是数组类型,避免循环引用(可加 visited Set 检测)
- 空值防御:始终检查 nodes 是否为数组,node.children 是否存在且为数组
- 性能考虑:深层嵌套时注意栈溢出风险;超大结构建议用栈模拟递归(迭代写法)
- 不可变性:如需保持原始数据不变,所有修改都应在新对象上进行(用展开运算符或 structuredClone)










