在前端开发、数据处理等场景中,经常会遇到多层嵌套的对象结构,比如接口返回的用户信息、配置项数据等,这些嵌套结构往往不方便直接进行遍历、查找或存储操作,此时就需要将嵌套对象中各层级的键值对扁平合并为单个无嵌套结构的对象,让所有属性都直接挂载在顶层对象上。

基础场景:仅处理对象嵌套,无数组混合
如果嵌套对象中只有对象类型的嵌套,没有数组、Map等其他复杂类型,我们可以通过递归遍历的方式实现扁平化。核心逻辑是遍历当前对象的所有属性,如果属性值是对象且不是null,就递归处理该属性值,将得到的扁平键值对的键拼接当前属性名作为新的键,否则直接将当前键值对放入结果对象中。
以下是具体的实现代码:
// 基础对象扁平化函数,仅处理对象嵌套场景
function flattenObject(obj, prefix = '', result = {}) {
// 遍历对象的所有自有属性
for (let key in obj) {
if (obj.hasOwnProperty(key)) {
// 拼接当前键的前缀,形成最终的键名
const newKey = prefix ? `${prefix}_${key}` : key;
// 判断当前值是否为对象且不是null,是则递归处理
if (typeof obj[key] === 'object' && obj[key] !== null && !Array.isArray(obj[key])) {
flattenObject(obj[key], newKey, result);
} else {
// 非对象值直接存入结果
result[newKey] = obj[key];
}
}
}
return result;
}
// 测试示例
const nestedObj = {
name: '张三',
age: 25,
address: {
province: '广东',
city: '深圳',
detail: {
street: '科技路',
number: 100
}
}
};
const flatObj = flattenObject(nestedObj);
console.log(flatObj);
// 输出结果:
// {
// name: '张三',
// age: 25,
// address_province: '广东',
// address_city: '深圳',
// address_detail_street: '科技路',
// address_detail_number: 100
// }
复杂场景:支持数组、多层混合嵌套
实际开发中嵌套对象可能会混合数组结构,比如某个属性的值是对象数组,这时候需要在基础逻辑上增加对数组的处理,遍历数组的每一项,给每一项拼接索引作为键的一部分,再递归处理数组项的内容。
以下是支持数组混合嵌套的扁平化实现:
// 支持数组混合的通用对象扁平化函数
function flattenComplexObject(obj, prefix = '', result = {}) {
// 处理数组类型
if (Array.isArray(obj)) {
obj.forEach((item, index) => {
const newKey = prefix ? `${prefix}_${index}` : `${index}`;
// 数组项如果是对象或数组,继续递归
if (typeof item === 'object' && item !== null) {
flattenComplexObject(item, newKey, result);
} else {
result[newKey] = item;
}
});
} else if (typeof obj === 'object' && obj !== null) {
// 处理对象类型
for (let key in obj) {
if (obj.hasOwnProperty(key)) {
const newKey = prefix ? `${prefix}_${key}` : key;
if (typeof obj[key] === 'object' && obj[key] !== null) {
flattenComplexObject(obj[key], newKey, result);
} else {
result[newKey] = obj[key];
}
}
}
} else {
// 基本类型直接赋值
result[prefix] = obj;
}
return result;
}
// 测试示例
const complexNestedObj = {
id: 1,
list: [
{ title: '第一项', value: 10 },
{ title: '第二项', value: 20 }
],
info: {
owner: '李四',
tags: ['前端', 'JavaScript']
}
};
const complexFlatObj = flattenComplexObject(complexNestedObj);
console.log(complexFlatObj);
// 输出结果:
// {
// id: 1,
// list_0_title: '第一项',
// list_0_value: 10,
// list_1_title: '第二项',
// list_1_value: 20,
// info_owner: '李四',
// info_tags_0: '前端',
// info_tags_1: 'JavaScript'
// }
实现注意事项
- 递归处理时需要注意判断值为null的情况,因为typeof null返回的是object,要避免把null当成对象递归处理。
- 键名拼接的规则可以根据实际需求调整,比如部分场景下可能需要用点号而不是下划线作为连接符,只需要修改拼接字符串的逻辑即可。
- 如果嵌套层级非常深,递归可能会导致栈溢出,此时可以考虑改用迭代的方式实现,用栈或队列存储待处理的对象,避免递归过深的问题。
迭代实现方式(避免深递归栈溢出)
如果处理的嵌套对象层级非常深,递归实现可能会出现栈溢出的问题,这时候可以用迭代的方式,通过栈来存储待处理的对象和当前的前缀信息,逐步处理所有层级的属性。
// 迭代方式实现对象扁平化,避免深递归栈溢出
function flattenObjectIterative(obj) {
const result = {};
// 栈中存储待处理的对象、当前前缀
const stack = [{ data: obj, prefix: '' }];
while (stack.length > 0) {
const { data, prefix } = stack.pop();
// 处理数组
if (Array.isArray(data)) {
data.forEach((item, index) => {
const newKey = prefix ? `${prefix}_${index}` : `${index}`;
if (typeof item === 'object' && item !== null) {
stack.push({ data: item, prefix: newKey });
} else {
result[newKey] = item;
}
});
} else if (typeof data === 'object' && data !== null) {
// 处理对象
for (let key in data) {
if (data.hasOwnProperty(key)) {
const newKey = prefix ? `${prefix}_${key}` : key;
if (typeof data[key] === 'object' && data[key] !== null) {
stack.push({ data: data[key], prefix: newKey });
} else {
result[newKey] = data[key];
}
}
}
} else {
// 基本类型
result[prefix] = data;
}
}
return result;
}
// 测试示例
const deepNestedObj = {
a: {
b: {
c: {
d: {
e: '深层值'
}
}
}
}
};
const iterativeFlatObj = flattenObjectIterative(deepNestedObj);
console.log(iterativeFlatObj); // { a_b_c_d_e: '深层值' }
对象扁平化嵌套对象处理JavaScript键值对合并修改时间:2026-06-28 18:54:35