
本文讲解如何编写一个纯递归函数,将具有多层嵌套结构的对象(如 main 和 offhand 分支)中所有字符串等原始值提取出来,并按原始顶层键名组织为对应数组,最终返回结构清晰的扁平化结果对象。
本文讲解如何编写一个纯递归函数,将具有多层嵌套结构的对象(如 `main` 和 `offhand` 分支)中所有字符串等原始值提取出来,并按原始顶层键名组织为对应数组,最终返回结构清晰的扁平化结果对象。
原问题的核心难点在于:在递归过程中错误地重复创建了 {fromMainHand: [], fromOffHand: []} 结构,导致每次递归调用都返回新对象而非原始值;同时未正确区分“遍历入口”与“值收集”的职责,造成数组中被推入的是对象而非字符串。
正确的思路是单次构造顶层结构,递归只负责向下提取原始值。以下是推荐的简洁、健壮且符合题意的单函数递归实现:
function devider(obj) {
// 基础情况:若为原始值(string/number/boolean/null/undefined),直接返回其数组形式
if (Object(obj) !== obj || obj === null) {
return [obj];
}
// 递归情况:对每个顶层键,递归处理其值,并展平所有嵌套结果
const result = {};
for (const key in obj) {
if (Object.prototype.hasOwnProperty.call(obj, key)) {
const nestedValues = devider(obj[key]);
result[key] = Array.isArray(nestedValues)
? nestedValues
: Object.values(nestedValues).flat();
}
}
return result;
}
使用示例:
const obj = {
main: {
value: { value: 'main value' },
anotherValue: 'some another value'
},
offhand: {
value: {
value: 'offhand value',
anotherValue: 'again recursion'
}
}
};
console.log(devider(obj));
// 输出:
// {
// main: ['main value', 'some another value'],
// offhand: ['offhand value', 'again recursion']
// }
✅ 关键设计说明:
-
Object(obj) !== obj是判断原始值(primitive)的可靠方式(涵盖string、number、boolean、null、undefined),比typeof更严谨(例如typeof null === 'object'是历史 bug); - 每次递归调用
devider(val)返回的是该子树下所有原始值的一维数组(或进一步递归生成的键值对象),上层通过.flat()统一展平,确保层级无关; - 顶层
for...in循环仅执行一次(在最外层输入对象上),避免在中间节点重复初始化fromMainHand/fromOffHand等硬编码键名,从而保持函数通用性与可扩展性; - 使用
hasOwnProperty防止意外遍历原型链属性,增强鲁棒性。
⚠️ 注意事项:
- 该函数不修改原对象,纯函数式设计,便于测试与组合;
- 若需严格限定仅提取
string类型(忽略数字、布尔等),可将基础 case 改为if (typeof obj === 'string') return [obj];,其余原始类型将被跳过; - 对于超深嵌套对象,注意浏览器调用栈限制;如需处理极大结构,可考虑转为显式栈的迭代写法。
此方案兼顾可读性、正确性与工程实用性,是一次「职责分离 + 递归收敛」的经典实践。










