JAVASCRIPT V2 · ADVANCED · 127
Map / Set / Identity:先理解「同一個值」到底是什麼
Array、Object、Map、Set 都能裝資料,但它們表達的問題不同。這一課的核心不是背 API,而是建立 primitive equality、object reference identity、uniqueness 與 key-value mapping 的正確模型。
Learning outcomes
- 區分 primitive equality 與 object reference identity。
- 依 problem shape 選 Array、Object、Map 或 Set。
- 用 Set 表達 uniqueness / membership。
- 用 Map 建立 explicit key-value index/cache。
- 理解淺複製不會遞迴複製 nested objects。
1. Why now:資料多了之後,Array 不一定是最自然的模型
const selectedIds = [1, 4, 9];
// 問:id 4 是否已選?
// 問:如何確保沒有重複?
如果核心問題是 membership 與 uniqueness,Set 的語意比「array 但我們約定不准重複」更精確。
2. Mental model:Primitive 比 value,Object 比 identity
1 === 1
// true
"JS" === "JS"
// true
{} === {}
// false
兩個 object literal 內容相同,仍是兩個不同 objects。
3. Worked Example A:Alias 與 Copy
const a = {
name: "Amy"
};
const b = a;
b.name = "Bob";
console.log(a.name);
// Bob
a 與 b 指向同一個 object identity。這不是「const 被改掉」,而是 object 內容被 mutation。
4. 淺複製建立新外層 object,但 nested reference 仍可能共享
const a = {
profile: {
city: "Tainan"
}
};
const b = { ...a };
b.profile.city =
"Kaohsiung";
console.log(
a.profile.city
);
// Kaohsiung
spread 只建立新的 outer object;profile reference 仍相同。
5. Worked Example B:Set 表達唯一集合
const tags =
new Set();
tags.add("javascript");
tags.add("javascript");
tags.add("debug");
console.log(tags.size);
// 2
tags.has("debug");
// true
Set 很適合「是否存在」「不重複集合」。如果你需要 index-based order operations,Array 仍可能更合適。
6. Set 對 Object 也依 Identity 去重
const a = { id: 1 };
const b = { id: 1 };
const set =
new Set([a, b]);
console.log(set.size);
// 2
Set 不會自動根據 id 欄位判斷「業務上相同」。如果 domain identity 是 id,應自己以 id 建索引。
7. Worked Example C:Map 做 ID index
const byId =
new Map(
students.map(
student => [
student.id,
student
]
)
);
const student =
byId.get(42);
Map 的 key 可以是 number、object 或其他 value;API 也直接表達 has/get/set/delete。
8. Object key 與 Map key 的語意差異
const obj = {};
obj[1] = "one";
console.log(
Object.keys(obj)
);
// ["1"]
const map = new Map();
map.set(1, "number");
map.set("1", "string");
console.log(map.size);
// 2
plain Object 仍非常適合固定 shape 的 record;Map 則更像真正的 dynamic key-value collection。
9. Common mistakes
A. 用 JSON.stringify 判斷所有 object equality
property ordering、unsupported values、cycles 與 domain semantics 都會讓這方法不可靠。
B. 以為 spread 是 deep clone
nested objects 仍可能共享 identity。
C. 用 Set 去除「相同 id 的 objects」
不同 object references 仍會同時存在。
D. 只因 Big-O 就選 Map
先看問題語意與資料量,再談 complexity。
10. Debug evidence:Reference bug 要印 identity 關係
console.log(
original === copy
);
console.log(
original.profile
=== copy.profile
);
看到「改 copy 卻污染 original」,先分層確認 outer identity 與 nested identity,而不是先怪 spread operator。
11. Guided exercise:Selected Student IDs
const selected =
new Set();
function toggle(id) {
// TODO
}
要求:存在就 delete,不存在就 add;回傳目前是否已選取。
12. Independent exercise:建立 Map index
從 student array 建立 id → student Map,支援 lookup、update index、delete。再說明如果 student.id 改了,index 需要如何維護。
function indexById(
students
) {
// return Map
}13. Challenge:Domain equality
兩筆 student objects 只要 id 相同就視為同一學生。請寫 dedupeById(students),不能依賴 Set 對 object 本身去重。
const input = [
{ id: 1, name: "Amy" },
{ id: 1, name: "Amy updated" }
];Project checkpoint:Grade Tracker Selection + Index
const selectedIds =
new Set();
let studentsById =
new Map();
function rebuildIndex(
students
) {
studentsById =
new Map(
students.map(
s => [s.id, s]
)
);
}
現在 UI selection 與 data lookup 各自有符合語意的資料結構,而不是所有需求都塞進一個 Array。
14. Vocabulary / Summary
- identity:某個 object 實體本身的識別。
- alias:兩個 bindings 指向同一 object。
- shallow copy:只複製最外層結構。
- Set:unique value collection。
- Map:explicit key-value collection。
- domain equality:由業務規則定義「算不算同一筆」。
15. Further reading
Knowledge check · Mastery
- 解釋為何
{id:1} === {id:1}是 false。 - 說明 shallow copy 的 nested reference 風險。
- 完成 Set toggle 與 Map index。
- 完成頁面下方 Executable JS identity tests。