JAVASCRIPT V2 · EVERYDAY · 59
Spread、Destructuring、Optional Chaining:安全讀寫 Reference Data
這些「現代語法」不是為了讓 code 看起來新。它們主要解決三件事:建立新 object/array 而不是意外共用 reference、清楚取出需要的 properties、在資料可能缺失時安全讀取。理解 shallow copy 與 fallback semantics 比背符號更重要。
Learning outcomes
- 解釋 object/array spread 的 shallow copy。
- 能做 nested immutable update 而不修改原資料。
- 能用 destructuring 清楚表達資料需求。
- 區分 optional chaining 的 crash protection 與資料 validity。
- 理解
??與||在 0/false/empty string 上的差異。
1. Why now:Reference aliasing 已經開始影響資料更新
const a = { score: 80 };
const b = a;
b.score = 90;
console.log(a.score); // 90
40 已經看到 aliasing。現在開始學「我要更新資料,但保留舊 reference state」的常用工具。
2. Object spread:建立新的 top-level object
const student = {
name: "Amy",
score: 80
};
const updated = {
...student,
score: 95
};
student !== updated,而且原本 student.score 仍是 80。
3. Spread 是 shallow copy,不是 deep clone
const user = {
profile: {
city: "Tainan"
}
};
const copy = { ...user };
copy.profile.city = "Taipei";
console.log(user.profile.city);
// Taipei
外層 object 是新的,但 nested profile reference 仍共用。
4. Worked Example A:Nested immutable update
const updated = {
...user,
profile: {
...user.profile,
city: "Taipei"
}
};
你要在哪一層改 property,就必須確保從 root 到該層需要獨立的 objects 都建立新 reference。
5. Array spread:建立新 array,但 element references 仍可能共用
const oldList = [
{ id: 1, score: 80 }
];
const nextList = [...oldList];
console.log(oldList === nextList); // false
console.log(oldList[0] === nextList[0]); // true
這就是 shallow copy 的另一個例子。
6. Destructuring:把資料需求寫在左邊
const {
name,
score
} = student;
function label({
name,
score
}) {
return name + ": " + score;
}
Destructuring 不會 clone 整個 object,也不會自動 validate properties;它只是建立 local bindings。
7. Rename / Default:面對不同命名或缺省值
const {
name: displayName,
score = 0
} = student;
default 只在 property 是 undefined 時使用;不是所有 falsy value 都會 fallback。
8. Optional chaining:安全停止 property access
const city =
user.profile?.address?.city;
中間某層是 null/undefined 時回 undefined,避免立即 TypeError。
9. ?? vs ||:合法的 0 / false 不應被吃掉
const score = 0;
score || 100; // 100
score ?? 100; // 0
|| 對所有 falsy value fallback;?? 只針對 null/undefined。
10. Worked Example B:安全讀取 + 合理 fallback
const city =
response.user?.profile?.city
?? "Unknown";
這代表「city 缺失可接受並顯示 Unknown」。如果 contract 規定 city 必須存在,就不應用 optional chaining 把錯誤完全藏掉。
11. Worked Example C:Array immutable update
function updateScore(
students,
id,
nextScore
) {
return students.map(student =>
student.id === id
? {
...student,
score: nextScore
}
: student
);
}
外層 array 是新的,被更新那一筆 object 也是新的;其他未變 records 可以保留原 reference。
12. Common mistakes
A. 以為 spread 是 deep clone
nested reference 仍可能共用。
B. 所有 property access 都加 ?.
可能把 schema regression 變成沉默的 undefined。
C. 用 || fallback 掉合法 0/false
如果只想處理 null/undefined,使用 ??。
D. Destructuring 後誤以為原 object 已改名/修改
只是 local bindings。
13. Debug evidence:檢查 reference equality
console.log({
sameRoot: oldUser === nextUser,
sameProfile:
oldUser.profile === nextUser.profile
});
當「我明明複製了,為什麼舊資料也改了」時,reference equality 是直接 evidence。
14. Guided exercise:判斷哪些 references 是新的
const a = {
profile: { city: "Tainan" }
};
const b = {
...a,
name: "Amy"
};
回答:a===b?a.profile===b.profile?修改 b.profile.city 會不會影響 a?
15. Independent exercise:Nested update
完成 moveUser(user,nextCity):回傳新 root object、新 profile object,其他欄位保留,原 user 不變。
16. Challenge:Update by ID
用 map + spread 更新 Student[] 中指定 id 的 score,並確認:
- newArray !== oldArray
- updatedStudent !== oldStudent
- unmodified records 可保留原 reference
Project checkpoint:Grade Tracker Immutable Update
function updateStudent(
students,
id,
patch
) {
return students.map(student =>
student.id === id
? { ...student, ...patch }
: student
);
}
這個 pattern 會直接進 React state,因此現在先把 reference semantics 學穩。
17. Vocabulary / Summary
- shallow copy:只建立新 top-level container。
- destructuring:從結構取值建立 local bindings。
- optional chaining:null/undefined 時停止 property access。
- nullish coalescing:只對 null/undefined fallback。
- immutable update:不直接修改舊資料,而建立新 state representation。
18. Further reading
Knowledge check
- 說明 shallow copy 為什麼不會複製 nested object。
- 比較
??與||對 0 的結果。 - 說明 optional chaining 解決的是 crash 還是 validity。
- 用 reference equality 驗證 nested update 是否真的建立新 object。
- 完成 nested immutable update mastery tests。