A06 数组与对象

2026-10-31 17:240 阅读约 12 分钟JavaScript
AI 摘要30 秒读完

讲解 JS 数组与对象的常用操作,重点说明 sort 原地修改陷阱与不可变写法,提供方法对照表、分组排序安全写法及常见问题排查。

  • 数组方法分原地修改与返回新数组两类,sort/reverse/splice 会污染原状态,应优先用 toSorted/toReversed
  • sort 默认按字符串比较,数字排序必须传比较函数 (a,b)=>a.key-b.key
  • 更新列表项用 map + 展开替换,删除对象字段用解构 rest,保持不可变
  • 分组优先 Object.groupBy,旧环境用 reduce 手写;展开复制仅一层,嵌套需逐层处理或 structuredClone
AI 生成 · 仅供参考

A06 数组与对象

摘要:数组存有序列表,对象存具名记录,两者构成页面数据的全部载体。数组的 map/filter/sort 与对象的展开、解构、Object.entries 决定代码能否保持不可变。sort 原地修改且默认按字符串比较,是最容易出错的两个细节。本文给出常用方法对照表、按字段分组与排序的安全写法。
关键词:数组,对象,解构,不可变,排序

所属:frontend/js / A-入门 | 上一篇:A05-函数与参数 | 下一篇:A07-字符串与数字处理

1 引言

列表页的典型需求是:过滤、排序、按字段分组、取一条详情、更新其中一项。用 for 循环逐条改写能实现,但原数组会被污染,UI 与状态容易不同步。

本文给出四类操作的标准写法:读取、转换、更新、聚合。范围见表 1-1。

项 本文覆盖 本文不覆盖
数组 增删改查、map/filter/sort/reduce 类型化数组
对象 展开、解构、entries、可选链 属性描述符(C02)
不可变 展开复制、toSorted/toReversed 深拷贝(C06)

2 预备知识

项 要求 核对方式
环境 Node.js 20+(含 toSorted) node -v 确认版本
前置 A05 能写纯函数
文件 js-practice/collections.js 编码 UTF-8

3 原理与实现

3.1 可变与不可变两组方法

数组方法分两类:一类原地修改,一类返回新数组,如图 3-1 所示。

flowchart LR
    A["原数组 arr"] --> B["原地修改<br/>push·splice·sort·reverse"]
    A --> C["返回新数组<br/>map·filter·slice·toSorted"]
    B --> D["arr 被改变<br/>旧引用同步变化"]
    C --> E["arr 不变<br/>旧引用保持原值"]

图 3-1 原地修改与返回新数组的方法分属两条路径

图上可见:sort 同时出现在原地修改一列,复制后再排序才能避免污染状态;toSorted 是返回新数组的替代品。

3.2 完整实现

// js-practice/collections.js;运行环境:Node.js 20+
const products = [
  { id: 3, name: '键盘', price: 299, category: 'input' },
  { id: 1, name: '显示器', price: 1299, category: 'output' },
  { id: 2, name: '鼠标', price: 99, category: 'input' },
];

// 1 排序:复制后排序,比较函数必须返回数字
const byPriceAsc = products.toSorted((a, b) => a.price - b.price);
console.log(byPriceAsc.map((p) => p.name));     // ['鼠标','键盘','显示器']
console.log(products[0].name);                  // 键盘,原数组未变

// 2 原地 sort 的默认行为是字符串比较
console.log([10, 9, 100].sort());               // [10, 100, 9]
console.log([10, 9, 100].sort((a, b) => a - b)); // [10, 9, 100]

// 3 过滤与映射链式组合
const cheapInputs = products
  .filter((p) => p.category === 'input' && p.price < 200)
  .map((p) => ({ ...p, label: `${p.name} ¥${p.price}` }));
console.log(cheapInputs);                       // [ { id: 2, name: '鼠标', price: 99, category: 'input', label: '鼠标 ¥99' } ]

// 4 按字段分组
const byCategory = Object.groupBy(products, (p) => p.category);
console.log(Object.keys(byCategory));           // ['input','output']

// 5 更新一项而不改原数组
const updated = products.map((p) => (p.id === 2 ? { ...p, price: 89 } : p));
console.log(updated[2].price, products[2].price); // 89 99

// 6 解构取值与可选链
const [{ name: firstName }, ...rest] = products;
const detail = products.find((p) => p.id === 9);
console.log(firstName, rest.length, detail?.name ?? '不存在'); // 键盘 2 不存在

// 7 对象遍历
for (const [key, value] of Object.entries({ a: 1, b: 2 })) {
  console.log(key, value);
}

常用方法对照见表 3-1。

需求 方法 是否改原数组 返回
追加 / 删除末尾 push / pop 是 长度 / 元素
删除指定位置 splice 是 被删元素数组
截取片段 slice 否 新数组
排序 sort / toSorted 是 / 否 原数组 / 新数组
反转 reverse / toReversed 是 / 否 原数组 / 新数组
查找索引 findIndex / indexOf 否 索引或 -1
判断包含 includes / some 否 布尔值
合并 concat / 展开 否 新数组

对象操作的等价写法见表 3-2。

目的 写法 说明
取安全属性 obj?.a?.b 空值短路,不抛错
给默认值 obj.a ?? 0 0 与 "" 保留
复制并覆盖 { ...obj, a: 1 } 后者覆盖前者
删除字段 const { a, ...next } = obj 生成不含 a 的新对象
键值列表 Object.entries(obj) 用于转表格、转 Map

4 验证与讨论

  1. 执行脚本,确认 byPriceAsc 顺序为 ['鼠标','键盘','显示器'],且原数组首项仍为键盘。
  2. 把 toSorted 改为 sort,再打印原数组,确认被改变。
  3. 注释 sort 的比较函数,观察 [10, 9, 100] 变为 [10, 100, 9]。
  4. 用 Object.groupBy 的分组结果渲染表格,确认分组键与数据一一对应。
检查项 预期结果 说明
不可变排序 原数组顺序不变 toSorted 返回新数组
数字排序 升序正确 必须传比较函数
链式过滤 只保留符合两项条件的记录 filter 支持复合条件
局部更新 仅目标项变化 map + 展开替换

讨论与局限。 Object.groupBy 需要较新运行时,旧环境需手写 reduce 分组。另,展开复制只处理一层,嵌套对象仍共享引用;sort 在元素数量大时不稳定以外的比较成本可忽略,但比较函数内做类型转换会显著变慢。

5 常见问题

现象 原因 处理
排序结果像随机 未传比较函数,按字符串比较 传 (a, b) => a.key - b.key
原数组被意外修改 用了 sort/reverse/splice 改用 toSorted 或先 slice()
对象展开后仍同步变化 嵌套对象未复制 逐层展开或用 structuredClone
遍历对象顺序不符合预期 依赖了属性顺序 用 Object.entries 后显式排序
find 返回 undefined 后报错 未判空 用 ?. 或先判 if (!item)

6 结论

序号 结论 依据
1 sort 原地修改且默认按字符串比较 3.2 节
2 保持不可变优先用 toSorted/toReversed 图 3-1
3 更新列表项用 map + 展开 3.2 节
4 分组优先 Object.groupBy,旧环境用 reduce 表 3-1

参考文献

[1] MDN Web Docs · Array,https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array(访问于 2026-10-31)
[2] MDN Web Docs · Object.groupBy,https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/groupBy(访问于 2026-10-31)

返回文章列表

评论 0

0/2000

还没有评论,快来抢沙发吧~