🐸 青蛙编程网
首页 / 前端 / JSON / 4. 嵌套对象与数组

嵌套对象与数组

这是什么:嵌套 = 数据套数据:对象里装数组、数组里装对象,一层层像俄罗斯套娃。真实接口返回的数据全是这种结构。

为什么用:接口返回的「用户列表」「商品列表」都是「数组套对象」,不会拆嵌套就拿不到数据;而且套得越深,取值越容易踩 undefined 的坑。

如何用:先学「一层层点下去」的取值,再学 for 循环遍历,最后用 ?. 可选链防报错。

多层取值:一路点下去

let data = {
  class: '蛙一班',
  teacher: { name: '蛙老师', room: 'B201' },
  students: [
    { name: '呱小吉', age: 3 },
    { name: '蛙大壮', age: 4 }
  ]
}

console.log(data.teacher.name)     // 蛙老师(对象点对象)
console.log(data.students[0].name) // 呱小吉(数组先按下标,再点属性)
console.log(data.students[1].age)  // 4

「你会看到」:规律就一句话——对象用 . 点属性,数组用 [下标] 取元素,从外往内一层层拆。

表达式 读法 结果
data.class data 的 class '蛙一班'
data.teacher.room data 的 teacher 的 room 'B201'
data.students[0] students 里的第 0 个 呱小吉那个对象
data.students[0].name 第 0 个学生的名字 '呱小吉'

遍历对象数组:for...of

拿到一个「学生列表」,最常用 for...of 逐个处理:

for (let stu of data.students) {
  console.log(`${stu.name} 今年 ${stu.age} 岁`)
}

你会看到:

呱小吉 今年 3 岁
蛙大壮 今年 4 岁

stu 依次是数组里的每个对象,循环体里正常点属性取值。要筛选就配 filter,要加工就配 map:

let names = data.students.map(stu => stu.name)
console.log(names)   // ['呱小吉', '蛙大壮']

遍历纯对象:Object.keys / Object.entries

for...of 只能遍历数组。想遍历对象的每个键值对,先把它变成数组:

let frog = { name: '呱小吉', age: 3, skill: '游泳' }

console.log(Object.keys(frog))               // ['name', 'age', 'skill']

for (let [key, value] of Object.entries(frog)) {
  console.log(`${key}: ${value}`)
}

你会看到:

name: 呱小吉
age: 3
skill: 游泳

Object.keys 拿所有键名,Object.entries 拿所有「键值对」——配置项打印、调试数据时特别好用。

?. 可选链:缺了也不报错

从深层取值最大的风险:中间某一层不存在,继续点就炸:

let user = { name: '呱小吉' }   // 注意:没有 address 这个键

console.log(user.address.city)      // ❌ TypeError: Cannot read properties of undefined
console.log(user.address?.city)     // ✅ undefined,不报错

?. 的意思是「这一层存在才继续往下取,不存在就返回 undefined」。接口数据不保证字段齐全,读深层字段时用 ?. 是最省心的防错手段(user?.address?.city 连第一层也能防)。

记忆口诀:对象点、数组下标,一层一层往里瞧;中间可能缺一环,问号点防摔跤。

📦 完整小示例

一份班级成绩单,统计平均分并找出最高分:

let report = {
  class: '蛙一班',
  scores: [
    { name: '呱小吉', score: 92 },
    { name: '蛙大壮', score: 85 },
    { name: '蛙萌萌', score: 98 }
  ]
}

let list = report.scores
let total = 0
let best = list[0]

for (let stu of list) {
  total = total + stu.score
  if (stu.score > best.score) {
    best = stu
  }
}

console.log(`${report.class} 共 ${list.length} 人,平均分 ${total / list.length}`)
console.log(`最高分:${best.name} ${best.score} 分`)

你会看到:

蛙一班 共 3 人,平均分 91.66666666666667
最高分:蛙萌萌 98 分

逐段中文精讲

  • 第 2~10 行(数据区):对象套数组、数组再套对象——接口数据的标准长相,先看清结构再动手。
  • 第 12~14 行(准备区):list 起个短名字少打字;total 从 0 开始累加;best 先假设第 0 个是冠军,后面遇强则换。
  • 第 16~21 行(循环区):for...of 逐个学生累加分数;stu.score > best.score 就换冠军——典型的「打擂台」算法。
  • 第 23~24 行(播报区):list.length 取人数,模板字符串拼结果。平均分有小数很正常,介意就用 .toFixed(1) 保留一位。

📝 本章小结

  • 取值规律:对象 . 属性,数组 [下标],从外往内拆
  • 数组遍历用 for...of / map / filter;纯对象用 Object.keys / Object.entries
  • 深层取值用 ?. 可选链,中间缺层返回 undefined 而不是报错
  • 先看结构、再写代码——拿到嵌套数据先打印出来看一眼

动手改

  1. 给 report.scores 再加一个学生,看看平均分和冠军怎么变
  2. 用 filter 挑出 90 分以上的学生
  3. 故意写 report.teachers[0].name,先看报错,再改成 ?. 写法让它安静返回 undefined

上一章:转换 · 下一章:常见报错与校验 🐸

📥 下载本章代码

ZIP 内含本章全部代码文件与说明,解压即用,与教程内容同步生成。