LESSON-15 · 自主延伸

陣列查找、複製與排序

把「陣列查找、複製與排序」拆成 4 個可獨立完成、可重設、可驗證的自主學習 module。

1. 現在要做什麼

成果:完成可逐筆顯示、查找、判斷並安全排序的服務資料流程。

需要的檔案:files/starter/index.html(整合起點,保留跨 module 的最小缺口。)

完成後:完成可逐筆顯示、查找、判斷並安全排序的服務資料流程。

0 / 8 個操作已記錄

2. 先備自我檢查

逐項展開;答不出來時先走補救連結,不必硬做。

已完成本章基礎練習並能找到 Starter。

自我檢查:我能否指出本章 Starter 入口與唯一 TODO?

不足時補救:回到本章 README,先完成基礎練習與入口導覽。

能開啟本機 HTML 並讀取至少一種瀏覽器證據。

自我檢查:我能否開啟 Elements、Console 或 Network 並找到本頁?

不足時補救:先開 DevTools,重新整理一次,確認目前文件與第一筆 request。

3. 完整學習目標與 Learning Map

每一個目標只對應一個 module;成功狀態必須能指出證據。

能以 for...of 依序讀取 iterable 的值,不把值與 index 混淆。

為什麼重要
以錯誤迴圈讀到 index 或 inherited key,會讓畫面顯示 0、1、2 而不是服務名稱。
完成條件
迭代三個服務物件並收集 name。
成功狀態
iterations=3、values=規劃/設計/上線,且輸出順序等於原陣列。
證據位置
iterations=3、values 順序正確

能分辨找第一筆符合資料與只回答是否存在。

為什麼重要
把 some 的 Boolean 當物件讀欄位,或只需存在卻回傳整筆資料,都會造成錯誤契約。
完成條件
對同一 featured=true 條件分別執行 find 與 some。
成功狀態
foundId=s2、exists=true、foundType=object、existsType=boolean。
證據位置
foundId=s2、exists=true、type 分別 object/boolean

能解構必要欄位並以 spread 建立不共用外層參照的副本。

為什麼重要
直接改原物件會污染資料來源;但誤以為 spread 是深拷貝,也會讓巢狀物件仍被共同修改。
完成條件
解構 id/name,建立 price 修改副本並比較原/副本參照。
成功狀態
copy!==original、original.price=1000、copy.price=1200、id/name 正確。
證據位置
sameReference=false、原價1000、副本1200

能在排序前先複製陣列,保留原始資料順序。

為什麼重要
sort 原地修改會讓其他元件、重設按鈕或快取同時失去原始順序。
完成條件
對服務名稱陣列先 spread,再只排序副本。
成功狀態
original=b,a,c、sorted=a,b,c、sameReference=false。
證據位置
original 未變、sorted 已排序、sameReference=false

4. 4 個知識模組與 Micro Labs

依序展開;每次只改一項,操作後確認該模組自己的 DOM、progress 與 evidence。

1for...of

完整解釋

白話:for...of 每一圈拿到的是目前那一筆值。

正式說法:for...of 對 iterable protocol 產生的 values 執行 statement,不直接提供索引。

何時使用:需要依序處理陣列、字串或其他 iterable,且不需要 index 時。

修改前後:Before:for...in 顯示 0/1/2。After:for...of 顯示三個服務名稱。

常見混淆:for...in 迭代 enumerable property keys;for...of 迭代 values。

名詞與最小範例

for...of for...of value iteration

白話
for...of 每一圈拿到的是目前那一筆值。
正式定義
for...of 對 iterable protocol 產生的 values 執行 statement,不直接提供索引。
何時使用
需要依序處理陣列、字串或其他 iterable,且不需要 index 時。
最小範例
for (const service of services) names.push(service.name)。
驗收證據
iterations=3、values 順序正確
常混淆
for...in 迭代 enumerable property keys;for...of 迭代 values。

Micro Lab|for...of

情境:服務清單畫面顯示 0、1、2。

起始狀態:輸出尚無值,範例誤用 property keys。

  1. 以 for...of 依序收集三個 service.name。
  2. 執行下方操作,觀察獨立的 DOM 與資料狀態。
  3. 依證據欄位重新驗收。

目前主題:for...of

尚未迭代
尚未執行;DOM data-state=starter。

預期:iterations=3、values=規劃/設計/上線,且輸出順序等於原陣列。

證據:iterations=3、values 順序正確

第一個檢查:先印出每圈變數,確認是 key 還是 value。

重設:清空輸出並恢復三筆原始資料。

卡住時

  • 症狀:輸出是 0、1、2。
    可能原因:使用 for...in。
    先檢查:輸出迴圈變數與 typeof。
    修正:改用 for...of,或以 index 回取 array[index]。

理解檢查

for...of 在陣列每圈提供什麼?

尚未作答。

查看解釋

它依 iterable protocol 讀 values。

本模組官方來源:MDN|for...of

2find() 與 some()

完整解釋

白話:find 拿回第一筆資料;some 只回答有或沒有。

正式說法:find 回傳第一個 predicate truthy 的 element 或 undefined;some 回傳是否至少一個 element 通過 predicate 的 Boolean。

何時使用:需要實際物件時用 find;只需判斷條件存在時用 some。

修改前後:Before:對 some 結果讀 id。After:物件與 Boolean 契約分開。

常見混淆:filter 會回傳所有符合項目的新陣列,不是第一筆或 Boolean。

名詞與最小範例

find() 與 some() Array find and some

白話
find 拿回第一筆資料;some 只回答有或沒有。
正式定義
find 回傳第一個 predicate truthy 的 element 或 undefined;some 回傳是否至少一個 element 通過 predicate 的 Boolean。
何時使用
需要實際物件時用 find;只需判斷條件存在時用 some。
最小範例
services.find(s => s.featured) 與 services.some(s => s.featured)。
驗收證據
foundId=s2、exists=true、type 分別 object/boolean
常混淆
filter 會回傳所有符合項目的新陣列,不是第一筆或 Boolean。

Micro Lab|find() 與 some()

情境:程式對 true.id 讀值而拋錯。

起始狀態:同一變數同時被當物件與 Boolean。

  1. 以同一 predicate 執行 find/some 並比較回傳型別。
  2. 執行下方操作,觀察獨立的 DOM 與資料狀態。
  3. 依證據欄位重新驗收。

目前主題:find() 與 some()

尚未查找
尚未執行;DOM data-state=starter。

預期:foundId=s2、exists=true、foundType=object、existsType=boolean。

證據:foundId=s2、exists=true、type 分別 object/boolean

第一個檢查:先問呼叫端需要資料本身還是只需要答案。

重設:恢復三筆服務與 featured 條件。

卡住時

  • 症狀:foundId 是 undefined。
    可能原因:使用 some 後把 Boolean 當物件。
    先檢查:輸出 typeof result 與 Array.isArray。
    修正:需要物件時改用 find。

理解檢查

只要知道是否有符合資料,應用哪個?

尚未作答。

查看解釋

some 的契約就是 Boolean。

本模組官方來源:MDN|Array.find()

3destructuring 與 spread copy

完整解釋

白話:解構拿出要用欄位;spread 先複製外層再覆蓋一個值。

正式說法:object destructuring binding 依 property 取值;object spread 建立 enumerable own properties 的淺層副本。

何時使用:從資料物件讀少數欄位,或建立不修改原外層物件的新版本。

修改前後:Before:service.price 直接被改。After:原物件與副本價格可並列。

常見混淆:spread 是 shallow copy,巢狀物件參照仍共享。

名詞與最小範例

destructuring 與 spread copy Destructuring and shallow spread copy

白話
解構拿出要用欄位;spread 先複製外層再覆蓋一個值。
正式定義
object destructuring binding 依 property 取值;object spread 建立 enumerable own properties 的淺層副本。
何時使用
從資料物件讀少數欄位,或建立不修改原外層物件的新版本。
最小範例
const {id,name}=service; const copy={...service,price:1200}。
驗收證據
sameReference=false、原價1000、副本1200
常混淆
spread 是 shallow copy,巢狀物件參照仍共享。

Micro Lab|destructuring 與 spread copy

情境:排序/編輯一張卡片後原始 API 資料也改了。

起始狀態:只有原始 service,尚無副本。

  1. 解構 id/name,再以 spread 建立新價格副本。
  2. 執行下方操作,觀察獨立的 DOM 與資料狀態。
  3. 依證據欄位重新驗收。

目前主題:destructuring 與 spread copy

尚未複製
尚未執行;DOM data-state=starter。

預期:copy!==original、original.price=1000、copy.price=1200、id/name 正確。

證據:sameReference=false、原價1000、副本1200

第一個檢查:先用 === 比較原物件與副本參照。

重設:恢復原始 price=1000。

卡住時

  • 症狀:改 copy.details 後 original.details 也變。
    可能原因:spread 只做淺層複製。
    先檢查:比較 original.details === copy.details。
    修正:需要獨立巢狀資料時也複製該層。

理解檢查

object spread 預設是哪種複製?

尚未作答。

查看解釋

巢狀 object/array 仍保留原參照。

本模組官方來源:MDN|Destructuring

4sort() 會修改原陣列

完整解釋

白話:sort 會直接重排手上的陣列;先複製才不會動到原資料。

正式說法:Array.prototype.sort() sorts elements in place and returns the same array reference。

何時使用:需要顯示排序結果但仍要保留 API/初始順序時。

修改前後:Before:排序後重設無法回原序。After:原陣列與排序副本並存。

常見混淆:toSorted 會回傳新陣列,但本課技術邊界先用 spread + sort。

名詞與最小範例

sort() 會修改原陣列 In-place Array sort and copied sorting

白話
sort 會直接重排手上的陣列;先複製才不會動到原資料。
正式定義
Array.prototype.sort() sorts elements in place and returns the same array reference。
何時使用
需要顯示排序結果但仍要保留 API/初始順序時。
最小範例
const sorted=[...items].sort(compareFn)。
驗收證據
original 未變、sorted 已排序、sameReference=false
常混淆
toSorted 會回傳新陣列,但本課技術邊界先用 spread + sort。

Micro Lab|sort() 會修改原陣列

情境:按價格排序後,取消排序仍回不去原始順序。

起始狀態:原陣列順序 b,a,c。

  1. 記錄原順序,spread 複製後排序,最後比較參照與兩個順序。
  2. 執行下方操作,觀察獨立的 DOM 與資料狀態。
  3. 依證據欄位重新驗收。

目前主題:sort() 會修改原陣列

尚未排序
尚未執行;DOM data-state=starter。

預期:original=b,a,c、sorted=a,b,c、sameReference=false。

證據:original 未變、sorted 已排序、sameReference=false

第一個檢查:排序前先保存原順序與參照比較。

重設:恢復原陣列並丟棄排序副本。

卡住時

  • 症狀:original 與 sorted 都變成 a,b,c。
    可能原因:直接對 original 呼叫 sort。
    先檢查:比較 sorted===original。
    修正:改成先 spread copy 再 sort。

理解檢查

sort() 對原陣列做什麼?

尚未作答。

查看解釋

它回傳同一個陣列參照。

本模組官方來源:MDN|Array.sort()

5. 陣列查找、複製與排序|整合 Lab

情境:在同一份章節作品中整合 4 個 module,排除單項通過但組合失敗的問題。

交付:完成可逐筆顯示、查找、判斷並安全排序的服務資料流程。

尚未執行整合步驟。
  1. 1觀察整合 Starter 並找出第一個未通過 module。

    檔案:files/starter/index.html 定位:#observe

    操作:逐項讀取 outcomes,執行一次現況操作。

    // OBSERVE:不修改程式,先記錄第一個失敗 moduleId。

    預期:能指出第一個未通過 module。

    證據:第一個 moduleId 與缺少的 evidence。

    原因:先觀察可避免未重現就直接改答案。

    卡住先查:確認開啟的是 files/starter/index.html。

  2. 2依 module 順序修改,一次只加入一個責任。

    檔案:files/starter/index.html 定位:#modify

    操作:以 for...of 依序收集三個 service.name。 → 以同一 predicate 執行 find/some 並比較回傳型別。 → 解構 id/name,再以 spread 建立新價格副本。 → 記錄原順序,spread 複製後排序,最後比較參照與兩個順序。

    // 15-01-for-of: 以 for...of 依序收集三個 service.name。
    // 15-02-find-some: 以同一 predicate 執行 find/some 並比較回傳型別。
    // 15-03-destructuring-spread-copy: 解構 id/name,再以 spread 建立新價格副本。
    // 15-04-sort: 記錄原順序,spread 複製後排序,最後比較參照與兩個順序。

    預期:完成可逐筆顯示、查找、判斷並安全排序的服務資料流程。

    證據:iterations=3、values 順序正確;foundId=s2、exists=true、type 分別 object/boolean;sameReference=false、原價1000、副本1200;original 未變、sorted 已排序、sameReference=false

    原因:逐項整合能把回歸定位到明確 module。

    卡住先查:回到第一個未通過的 module,只修該處。

  3. 3驗證所有 module 在整合後仍成立。

    檔案:files/solution/index.html 定位:#verify

    操作:重新整理並依 outcomes 順序重跑所有證據。

    // VERIFY:逐項記錄 pass/fail,不以看到畫面代替 evidence。

    預期:4/4 module 通過。

    證據:每個 moduleId 都有實際結果與重設後重現紀錄。

    原因:單項通過不代表整合後沒有 selector、id 或狀態衝突。

    卡住先查:若失敗,回到第一個失敗 module,不同時修多項。

  4. 4把本章責任轉用到不同內容情境。

    檔案:files/starter/index.html 定位:#challenge

    操作:替另一個商家情境重做,仍達成:完成可逐筆顯示、查找、判斷並安全排序的服務資料流程。

    // CHALLENGE:只替換內容與資料,不新增框架或跳過 self-check。

    預期:完成可逐筆顯示、查找、判斷並安全排序的服務資料流程。

    證據:轉用後的作品與全數 module self-check。

    原因:能轉用才代表理解責任,而非只記住原範例。

    卡住先查:先確認共同基底與 module id 未被改壞。

重設方法:重新載入 files/starter/index.html;若 localStorage 不可用,使用頁面內記憶狀態並提供重設按鈕。

6. 自學挑戰與三層提示

挑戰:將同一組責任轉用到另一個商家情境,仍達成:完成可逐筆顯示、查找、判斷並安全排序的服務資料流程。

限制:不新增框架。;保留 module id 與驗收證據。

提交證據:提交可直接開啟的作品與逐項 self-check。

提示 1|方向提示

拆小:在「陣列查找、複製與排序」先完成第一個尚未通過的 module:for...of,不同時修改其他責任。

提示 2|關鍵片段

串接:依 15-01-for-of → 15-02-find-some → 15-03-destructuring-spread-copy → 15-04-sort 核對各自輸入與證據。

提示 3|完整解答與原因

完整解答與原因:依序執行 以 for...of 依序收集三個 service.name。 → 以同一 predicate 執行 find/some 並比較回傳型別。 → 解構 id/name,再以 spread 建立新價格副本。 → 記錄原順序,spread 複製後排序,最後比較參照與兩個順序。;這個順序能把每個結果對回單一 module,避免整合後無法定位。

完整解答預設收合;先留下自己的嘗試,再開第三層。

7. 症狀式除錯指南

8. 理解測驗、驗收證據與下一步

哪個狀態代表 15-01-for-of 完成?

尚未作答。

查看解釋

以錯誤迴圈讀到 index 或 inherited key,會讓畫面顯示 0、1、2 而不是服務名稱。

哪個狀態代表 15-02-find-some 完成?

尚未作答。

查看解釋

把 some 的 Boolean 當物件讀欄位,或只需存在卻回傳整筆資料,都會造成錯誤契約。

哪個狀態代表 15-03-destructuring-spread-copy 完成?

尚未作答。

查看解釋

直接改原物件會污染資料來源;但誤以為 spread 是深拷貝,也會讓巢狀物件仍被共同修改。

哪個狀態代表 15-04-sort 完成?

尚未作答。

查看解釋

sort 原地修改會讓其他元件、重設按鈕或快取同時失去原始順序。

驗收清單

官方來源

回到課程地圖選擇下一章