4.3 数组与对象:工单数据的载体


4.3 数组与对象:工单数据的载体

本节摘要:数组装一批工单、对象装一张工单的全部字段,两者嵌套成看板的数据层。本节讲数组方法链(过滤、映射、归并)如何替代手写循环、对象的增删改查与遍历、解构与展开的搬运语法,以及"数据与页面分家"的关键转折——此后看板的一切界面变化都从这份数据推导。

一批工单与一张工单

看板的数据需求天然是两层:一张工单有编号、产品、数量、状态等字段,用对象装;一天的所有工单排成序列,用数组装。数组与对象的嵌套是前端最常见的数据形态,服务器接口吐回来的数据十有八九也是这个结构。本节先把这层"数据地基"打牢——后面 4.4 节渲染页面、4.6 节请求服务器、4.8 节存进本地,操作的都是这一份数据。

// 看板的数据地基:工单数组 const orders = [ { no: "MO-1042", product: "凸轮轴", process: "精铣", owner: "陈师傅", qty: 40, priority: "urgent", status: "running" }, { no: "MO-1043", product: "导套", process: "热处理", owner: "刘师傅", qty: 120, priority: "normal", status: "pending" }, { no: "MO-1044", product: "芯轴", process: "终检", owner: "赵师傅", qty: 8, priority: "urgent", status: "pending" }, { no: "MO-1045", product: "阀体", process: "精铣", owner: "陈师傅", qty: 60, priority: "normal", status: "done" }, ]; // 数组 = 一批工单;每个元素是对象 = 一张工单的全部字段 const order = orders[0]; // 下标访问:数组的第一项 order.product; // 点访问:对象的字段 order["pro" + "cess"]; // 方括号访问:字段名动态拼接时用
// 对象的增删改查与遍历 const stats = {}; stats["精铣"] = 100; // 增:方括号或点语法皆可 stats["热处理"] = 60; stats["精铣"] += 40; // 改:重新赋值 delete stats["热处理"]; // 删:整个字段消失 console.log("精铣" in stats); // 查:字段是否存在 → true for (const key in stats) { // 遍历:键的循环 console.log(key, stats[key]); } Object.keys(stats); // 全部键 → ["精铣"] Object.values(stats); // 全部值 → [140] Object.entries(stats); // 键值对数组 → [["精铣", 140]]

方法链:把循环写成流水线

数组自带一批"模式化加工"的方法:筛选、变形、汇总、查找。它们接收回调(4.2 节的原料函数),串成链后读起来像一句工序描述——"从工单里筛出加急的,摘出产品名,拼成一行警示"。方法链替代手写循环的收益不只是短,更重要的是每一步的意图都有名字。

// 过滤:加急工单名单 const urgent = orders.filter((o) => o.priority === "urgent"); // filter:留下回调判真的项,原数组不动,返回新数组 // 映射:把工单对象转成显示文本 const labels = urgent.map((o) => `${o.no} ${o.product} × ${o.qty}`); // map:每项过一遍回调,产出同长度的新数组 // 归并:加急件总量 const urgentQty = urgent.reduce((sum, o) => sum + o.qty, 0); // reduce:从初始值 0 起滚动累计,收敛成单个结果 console.log(labels, urgentQty); // ["MO-1042 凸轮轴 × 40", "MO-1044 芯轴 × 8"] 与 48 // 三连方法链:一行读完一道工序 const warnLine = orders .filter((o) => o.status === "pending" && o.priority === "urgent") .map((o) => `${o.product} 待开工`) .join(";"); // "凸轮轴 待开工;芯轴 待开工"——join 把数组拼成字符串
// 查找与判断:早退型的两个利器 const firstUrgent = orders.find((o) => o.priority === "urgent"); // find:找到第一个满足者就返回它,没有则 undefined const hasPending = orders.some((o) => o.status === "pending"); // some:有一个满足即真,全不满足才假——巡检式判断 const allDone = orders.every((o) => o.status === "done"); // every:全部满足才真——验收式判断 const byNo = orders.findIndex((o) => o.no === "MO-1044"); // findIndex:返回下标而非元素,删除或替换前先定位 // 修改数据的两种姿势 const next = orders.map((o) => o.no === "MO-1043" ? { ...o, status: "running" } : o ); // 不可变更新:展开旧对象、覆盖目标字段,生成新数组 // 原数组原封不动——框架与历史记录都依赖这个习惯 orders.sort((a, b) => b.qty - a.qty); // 排序:比较函数返回差值,注意 sort 原地改数组,链式前先复制

第二段代码里"不可变更新"的姿势值得专门消化:替换某张工单的状态时,不是直接改老对象,而是展开旧的、覆盖新的,生成一张新工单。看起来绕,实际是给未来上框架铺路——框架靠"数据变了没"决定要不要更新界面,原地暗改它看不见,展开复制它一眼认出。

统计实战:从原始数据到看板卡片

统计卡片是看板的脸面,数据全从工单数组推导。把 4.2 节的函数与方法链合起来,写出看板的统计模块。

// 统计模块:三张卡片的数字一次算齐 function summarize(orders) { const count = (s) => orders.filter((o) => o.status === s).length; return { pending: count("pending"), running: count("running"), done: count("done"), urgentPending: orders.filter((o) => o.priority === "urgent" && o.status === "pending").length, totalQty: orders.reduce((s, o) => s + o.qty, 0), }; } const s = summarize(orders); console.log(s); // { pending: 2, running: 1, done: 1, urgentPending: 2, totalQty: 228 } // 按工序分组的直方图数据 function byProcess(orders) { const groups = {}; for (const o of orders) { groups[o.process] = (groups[o.process] ?? 0) + o.qty; // 空值合并:首次遇到该工序从 0 起算 } return groups; } console.log(byProcess(orders)); // { 精铣: 100, 热处理: 120, 终检: 8 }
统计模块的走查(对照输出逐条核对): - 四张工单:待开工二、加工中一、已完工一,加急待开工二——与源数据一致 - 总数量二百二十八 = 40+120+8+60,归并初始值必须是 0,漏写会把首项当初始值 - 分组对象键是动态生成的工序名——用方括号语法写入,点语法写不了动态键

解构与展开:数据搬运的省力杠杠

解构按形状"拆箱取物",展开按形状"装箱铺开"。两者让函数签名与数据搬运的代码量大减,也是 4.7 节现代语法的重头,这里先用起来。

// 解构:函数参数直接拆对象 function printCard({ product, qty, status }) { // 参数位置解构:调用方传整个工单对象,函数体直接用字段名 console.log(`${product} × ${qty}:${status}`); } printCard(orders[0]); // 凸轮轴 × 40:running // 剩余收集与展开铺放 const { no, ...rest } = orders[1]; // 剩余收集:no 单独取出,其余字段收进 rest console.log(no, rest.product); // MO-1043 导套 const updated = { ...rest, qty: 150, note: "补单" }; // 展开铺放:rest 的字段全铺开,再覆盖两个新值 const withHeader = ["MO-1041", ...orders.map((o) => o.no)]; // 数组也能展开:前插一个编号生成完整序号表

数据与页面分家:本节的真正转折

到此为止,看板有了独立的数据层:工单数组加统计函数。下一节渲染页面时,代码将遵循一条新纪律——页面是数据的投影:界面要变,先改数据,再统一重画,而不是东一处西一处地直接抠页面文字。这条纪律在本册剩余部分不再动摇,它也是第 6 章一切框架的思想内核(框架不过是把"投影"自动化了)。

⚠️ 常见坑:混用会原地修改的方法与不可变习惯。排序、反转会改原数组,过滤、映射返回新数组——同一条链里混着用,数据何时被动过全凭记忆。纪律:要么全不可变(改前先复制),要么把原地修改集中在明确的"变更函数"里。

💡 关键直觉:拿到数据先问"形状",形状决定工具。一批同构记录用数组方法链,一张记录的字段操作用对象语法,键值统计用空对象累加——先认形状再动手,方法选择自动完成。

本节要点回顾

  • 两层结构:数组装一批工单、对象装一张工单,嵌套形态是接口数据的通行标准;
  • 方法链:过滤、映射、归并各有语义名字,链式读法接近工序描述;
  • 早退利器:查找与判断场景用查找某项、某项满足、全部满足,别再手写循环;
  • 不可变更新:展开旧对象覆盖新字段,原地暗改是框架时代的反模式;
  • 解构展开:拆箱取物与装箱铺放,函数签名与数据搬运的省力杠杠;
  • 投影纪律:页面是数据的投影,改界面先改数据再统一重画。

数据层就绪,下一节把它投影到页面上:文档对象模型操作,行为班的主战场。


作者与出处
原作者: 灏天文库
来源:灏天文库
整理: 灏天文库整理
由灏天文库平台收录,内容或由平台用户上传,仅供学习交流
发布者: 作者: 灏天文库 转发
评论区 (0)
U