第三十五天:Set 与 Map — ES6 新数据结构
蜗牛往上爬 · 前端工业可视化学习笔记 第 35 篇
数组和对象用了很多年,ES6 带来了两个新成员:Set(唯一值集合,去重神器)和 Map(真正的键值对,键可以是任何类型)。今天学它们 + 数据结构选型 + 一个综合实战:设备状态管理器。
目录
一、Set:唯一值集合
Set 中的值不重复——天然去重:
const set = new Set();
set.add("CNC-001");
set.add("AGV-002");
set.add("CNC-001"); // 重复,无效
console.log(set.size); // 2
set.has("CNC-001"); // true(判断存在)
set.delete("AGV-002");
// set.clear() 清空
// 初始化时传数组(自动去重)
const idSet = new Set(["CNC-001", "AGV-002", "CNC-001"]);
console.log(idSet.size); // 2
二、数组去重(经典用途)
const ids = ["CNC-001", "AGV-002", "CNC-001", "ARM-003", "AGV-002"];
// 一行去重
const unique = [...new Set(ids)];
// ["CNC-001", "AGV-002", "ARM-003"]
// 对象数组按字段去重(借助 Set 记录已见过的值)
const devices = [
{ id: "A", workshop: "一号" },
{ id: "B", workshop: "一号" },
{ id: "C", workshop: "二号" }
];
const seen = new Set();
const uniqueWorkshops = devices.filter(d => {
if (seen.has(d.workshop)) return false;
seen.add(d.workshop);
return true;
});
// [{ id: "A" }, { id: "C" }](每车间保留一台)
三、集合运算
const a = new Set([1, 2, 3]);
const b = new Set([2, 3, 4]);
// 并集
const union = new Set([...a, ...b]); // {1,2,3,4}
// 交集
const intersect = new Set([...a].filter(x => b.has(x))); // {2,3}
// 差集(a 有 b 没有)
const diff = new Set([...a].filter(x => !b.has(x))); // {1}
四、Map:真正的键值对
Map vs 对象:对象键只能是字符串,Map 的键可以是任何类型:
const map = new Map();
map.set("name", "CNC-001"); // 字符串键
map.set("temp", 65);
// ⭐ 对象键(对象做不到!)
const deviceKey = { id: "CNC-001" };
map.set(deviceKey, { temp: 65, status: "运行中" });
map.get("name"); // "CNC-001"
map.get(deviceKey); // { temp: 65, status: "运行中" }
map.get("xxx"); // undefined
map.has("temp"); // true
map.size; // 3
map.delete("temp");
五、Map 的遍历与转换
const statusMap = new Map([
["运行中", 12],
["待机", 5],
["故障", 2]
]);
// for...of + 解构(默认遍历 entries)
for (const [key, value] of statusMap) {
console.log(`${key}: ${value} 台`);
}
// keys / values
[...statusMap.keys()]; // ["运行中", "待机", "故障"]
[...statusMap.values()]; // [12, 5, 2]
// 求和
const total = [...statusMap.values()].reduce((s, n) => s + n, 0); // 19
// ===== 与对象互转 =====
// 对象 → Map
const map2 = new Map(Object.entries({ id: "CNC-001", temp: 65 }));
// Map → 对象
const obj = Object.fromEntries(map2);
词频统计(Map 经典应用):
// 统计各设备告警次数
const logs = [
{ device: "CNC-001" }, { device: "AGV-002" },
{ device: "CNC-001" }, { device: "ARM-003" },
{ device: "CNC-001" }
];
const counter = new Map();
for (const { device } of logs) {
counter.set(device, (counter.get(device) || 0) + 1);
}
console.log([...counter]);
// [["CNC-001", 3], ["AGV-002", 1], ["ARM-003", 1]]
六、数据结构选型
选型决策:
有顺序的一组数据 → 数组
描述一个东西 → 对象
要不重复 / 只判断存在 → Set
键是动态的或非字符串 → Map
性能提示:set.has() 是 O(1),arr.includes() 是 O(n)——大数据量频繁判断存在,用 Set。
七、综合实战:设备状态管理器
/**
* 设备状态管理器:Map 存状态 + Set 管告警/监控
*/
function createDeviceManager() {
const devices = new Map(); // 设备表:id → 数据
const monitored = new Set(); // 监控名单
const alertSet = new Set(); // 告警集合(自动去重)
return {
register(id, info = {}) {
if (devices.has(id)) return false;
devices.set(id, { id, name: info.name ?? "未知设备",
temp: info.temp ?? 0, status: info.status ?? "离线", ...info });
return true;
},
update(id, data) {
const device = devices.get(id);
if (!device) return console.warn(`${id} 未注册`);
devices.set(id, { ...device, ...data });
// 温度告警
const updated = devices.get(id);
if (updated.temp >= 80) {
alertSet.add(id);
console.warn(`⚠ ${id} 温度告警:${updated.temp}°C`);
} else {
alertSet.delete(id);
}
},
watch(id) { monitored.add(id); },
unwatch(id) { monitored.delete(id); },
get(id) { return devices.get(id) ?? null; },
getAlerts() { return [...alertSet]; },
// 词频统计:各状态数量
getStatusCount() {
const counter = new Map();
for (const { status } of devices.values()) {
counter.set(status, (counter.get(status) || 0) + 1);
}
return Object.fromEntries(counter);
},
// 监控且在线的设备(可选链安全访问)
getMonitoredOnline() {
return [...monitored].filter(id =>
devices.get(id)?.status === "运行中");
}
};
}
// 使用
const manager = createDeviceManager();
manager.register("CNC-001", { name: "数控机床", temp: 65, status: "运行中" });
manager.register("AGV-002", { name: "搬运机器人", temp: 42, status: "运行中" });
manager.watch("CNC-001");
manager.update("CNC-001", { temp: 85 }); // ⚠ 触发告警
console.log(manager.getAlerts()); // ["CNC-001"]
console.log(manager.getStatusCount()); // { "运行中": 2 }
manager.update("CNC-001", { temp: 60 }); // 恢复
console.log(manager.getAlerts()); // []
这个管理器用上了:Map(动态键值)、Set(唯一集合)、展开(合并更新)、??(兜底)、?.(安全访问)、词频统计——本周全部知识。
八、总结
🎯 Set 和 Map 补齐了数据结构版图。下周进入 ES6 的重头戏:异步编程(Promise / async await)——大屏数据请求的基础!