本文介绍一种简洁可靠的递归算法,用于遍历任意深度的嵌套数组结构,并为每个子元素动态注入 parentId 字段,精准指向其直接父元素的 id,解决深层嵌套下父子关系丢失的问题。
本文介绍一种简洁可靠的递归算法,用于遍历任意深度的嵌套数组结构,并为每个子元素动态注入 `parentid` 字段,精准指向其直接父元素的 `id`,解决深层嵌套下父子关系丢失的问题。
在处理树形数据(如菜单、组织架构、评论回复链)时,原始数据常以嵌套数组形式存在,但缺乏显式的父子引用关系。此时,为每个子节点补全 parentId 是后续扁平化、查找、拖拽排序或服务端同步的关键前提。
核心思路是:对每一层数组执行统一操作——为当前层每个元素设置 parentId,再对其 children 子数组递归调用同一逻辑。关键在于确保递归调用后继续遍历同层其余元素,而非提前返回。
以下是修正后的完整实现:
/**
* 递归为嵌套数组中所有子元素添加 parentId
* @param {Array} arr - 待处理的嵌套数组(原地修改)
* @param {string|number} parentId - 当前层级所有元素应继承的父ID(根层可传 null 或 undefined)
* @returns {Array} 返回处理后的数组(即原数组引用)
*/
const linkNestedElements = (arr, parentId) => {
for (const item of arr) {
// 为当前元素设置父ID(根元素 parentId 为 undefined,符合语义)
item.parentId = parentId;
// 若存在子节点,则递归处理,并将当前元素的 id 作为下一层的 parentId
if (Array.isArray(item.children) && item.children.length > 0) {
linkNestedElements(item.children, item.id);
}
}
return arr; // ✅ 必须放在循环外部,确保遍历完整层
};
// 使用示例
const treeData = [
{ id: '1', name: 'Root' },
{
id: '2',
name: 'Parent A',
children: [
{ id: '2-1', name: 'Child A1' },
{
id: '2-2',
name: 'Child A2',
children: [
{ id: '2-2-1', name: 'Grandchild A2-1' }
]
}
]
}
];
linkNestedElements(treeData); // 原地修改,无需重新赋值
// 验证结果:
console.log(treeData[1].children[0].parentId); // '2'
console.log(treeData[1].children[1].children[0].parentId); // '2-2'
⚠️ 注意事项:
- 该函数采用原地修改(in-place) 策略,不创建新对象,节省内存;若需不可变更新,请先深拷贝数组。
- parentId 对根级元素设为 undefined(非空字符串或 null),语义更清晰,便于后续条件判断(如 if (node.parentId !== undefined))。
- 检查 children 时使用 Array.isArray(item.children) && item.children.length > 0,比仅判 item.children && item.children.length 更健壮,避免 children: null 或 children: {} 导致意外行为。
- 时间复杂度为 O(n),其中 n 是所有节点总数;空间复杂度为 O(d),d 为最大嵌套深度(递归调用栈)。
此算法轻量、通用、无依赖,适用于任何符合 { id, children?: [...] } 结构的嵌套数据,是前端构建树形组件或预处理 API 响应的实用基础工具。










