# Day 11:JavaScript - 箭頭函式、toThrow、DOM/BOM、while loop **Published by:** [雞蛋糕的前端修煉屋](https://paragraph.com/@gcake/) **Published on:** 2025-12-04 **URL:** https://paragraph.com/@gcake/day-11 ## Content 補充概念:箭頭函式 Arrow function目前練習到的內容,在兩個地方大量用到箭頭函式的形式呼叫方法:TDD 使用 Vitest 的 test, describe, it => expect 等方法陣列 map 方法// 陣列方法的 callback const numbers = [1, 2, 3]; const doubled = numbers.map((n) => n * 2); // Vitest 基本語法 test('adds 1 + 2 to equal 3', () => { expect(sum(1, 2)).toBe(3) }) // 測試裡包一層給 toThrow 用 expect(() => divideBy3Times(3.5)).toThrow('n must be a positive integer'); 什麼是「語法糖」語法糖(syntactic sugar)指的是:語言提供的「更好寫、更好讀」的語法,沒有新增新的能力,只是讓原本就做得到的事情更順手。如果拿掉語法糖,還是可以用比較囉嗦的原始語法寫出一樣的邏輯。箭頭函式當語法糖時的使用情境在沒有用到 this / arguments / new 的情況下,箭頭函式可以直接當成「短版匿名函式」來用,例如:// 傳統匿名函式 const add = function (a, b) { return a + b; }; // 箭頭函式(語法更精簡) const add = (a, b) => { return a + b; }; // 進一步縮寫 const add = (a, b) => a + b; 這些情境裡,箭頭函式的角色:不會用到 this。只是讓「宣告一個小小的匿名函式」變得比較短、比較可讀。可以安全地把它看成「function () { ... } 的縮寫版」。註記:等學到 this 再回來看行為差異箭頭函式在 this、arguments、new 等行為上,和傳統函式其實有實質差異,這部分會在之後學到 this、物件方法、建構函式時再深入處理。目前階段先把箭頭函式當成「短版匿名函式」使用即可; 等學到 this 時,再回來補「箭頭函式與一般函式在行為上的差異」。Vitest:為什麼 expect 搭配 toThrow 要用箭頭函式包一層?在 Vitest / Jest 這類測試框架中,要驗證「呼叫某個函式會丟出錯誤」時,常看到這種寫法:const call = () => divideBy3Times(3.5); expect(call).toThrow('n must be a positive integer'); 初學時覺得這個寫法很不直覺,練習 TDD 時跟 happy path 一樣直接代入參數呼叫函式又讓測試因為報錯直接中斷,所以有兩個疑問:為什麼不能直接寫 expect(divideBy3Times(3.5)).toThrow()?箭頭函式 () => ... 在這裡到底扮演什麼角色?直接呼叫 vs 包成函式寫法一:直接呼叫(錯誤示範)expect(divideBy3Times(3.5)).toThrow(); // ❌ 執行順序:JavaScript 會先評估 expect(...) 的參數,這裡是 divideBy3Times(3.5)。divideBy3Times(3.5) 會立刻被執行。如果函式內有 throw new Error(...),錯誤會直接往外丟出,整個測試函式在進入 expect 之前就已經中斷。toThrow() 根本沒機會「幫你檢查」這個錯誤是不是預期中的錯誤。結果:測試會因為「未被捕捉的例外」而失敗,而不是因為 toThrow 的判斷失敗。寫法二:包成箭頭函式(正確示範)const call = () => divideBy3Times(3.5); expect(call).toThrow('n must be a positive integer'); // ✅ 執行順序:call 是一個函式本身,還沒被呼叫。expect(call) 把「函式本體」當作參數傳進去,而不是傳「呼叫結果」。toThrow() 的實作會在內部呼叫 call(),並在這個呼叫過程中監聽有沒有錯誤被丟出來。如果有錯誤丟出來,toThrow 會比對錯誤類型、訊息內容等,決定這個測試要標記為通過或失敗。關鍵差異:直接呼叫:錯誤在 expect 外就爆掉了。包成函式:錯誤被「延後」到 toThrow 內部才被觸發,讓測試框架有機會檢查這個錯誤是不是你預期的。箭頭函式在這裡的真正角色就語言層面來說,箭頭函式只是其中一種函式表達式(function expression):// 一般函式表達式 function call () { return divideBy3Times(3.5); } // 箭頭函式寫法 const call = () => divideBy3Times(3.5); 在這個場景下,兩種寫法的「語意角色」是一樣的:把「呼叫 divideBy3Times(3.5)」這件事包裝成一個可以稍後再執行的函式。並把這個函式「交給 expect().toThrow()」去呼叫與檢查。換句話說: 這裡箭頭函式的重點不是「this 綁定」或「語法糖」,而是用來做延遲執行(lazy execution):不馬上執行 divideBy3Times(3.5)。先把「要做這件事」改寫成一個函式,讓 toThrow 接管呼叫時機。連回 TDD 思維想驗證「值」:expect(函式呼叫結果).toBe(...)想驗證「會不會丟錯」:expect(包起來的函式).toThrow(...)寫測試時區分:「這次測試要檢查的是回傳值,還是行為(丟錯這件事本身)」,而箭頭函式在這裡就是拿來幫你把「行為」包裝成一個可以交給 expect 的第一級函式。資料結構《圖解資料結構 - 使用 JavaScript》第一章:程式設計、演算法和資料結構的關係JavaScript 屬於物件導向語言 (OOP),用函式定義屬性和行為第二章:用陣列包陣列表示矩陣,以及用一維矩陣壓縮稀疏矩陣的方法用一維陣列表達多項式的冪次和係數瀏覽器的物件模型:DOM 和 BOM《008天重新認識 JavaScript》第三天 瀏覽器上的 JavaScript 包含:JavaScript 核心 (ECMAScript 標準)BOM (Browser Object Model)DOM (Document Object Model)BOM 控制「瀏覽器本身」:視窗、網址列、導覽相關資訊。 DOM 控制「網頁內容」:節點、文字、屬性、事件等等。圖片來源 BOMwindow 是 JavaScript 跟瀏覽器溝通的窗口DOMDOM tree from w3schoolsJavaScript 可透過呼叫文件的屬性、標籤、名稱等方法控制網頁內容練習題:while loop找每次循環除法的商小數點後的特定位數的數值「在不知道要跑幾次之前,使用 while (true) 這種無限迴圈,配合每一輪的條件判斷決定什麼時候 break;在每一輪裡,把 number 用 toFixed 轉成固定小數位數的字串,再用 indexOf 找出小數點位置,透過字串索引來抽出想要的那一位數字。」toFixed 方法toFixed(2) 是 Number 的方法,回傳「固定小數點後 2 位」的字串。會做四捨五入,並補零到指定位數。範例:10 .toFixed(2) → "10.00"(3.3333).toFixed(2) → "3.33"(0.004).toFixed(2) → "0.00"indexOf 方法str.indexOf() 是 String 的方法,,會回傳「指定子字串第一次出現的索引」,如果找不到則回傳 -1。 參考文件來源Number.prototype.toFixed() - JavaScript | MDN用 try...catch...finally 流程控制顯示使用者友善訊息在終端機互動時顯示自訂訊息import { divideBy3Times } from './q9.js'; import readline from 'node:readline/promises'; import { stdin as input, stdout as output } from 'node:process'; const rl = readline.createInterface({ input, output }); async function main () { const num = await rl.question('請輸入一個正整數:'); const n = parseInt(num, 10); try { const times = divideBy3Times(n); console.log(`${n} 至少要除以 3 連續 ${times} 次,小數點後第二位四捨五入後是零`); } catch (error) { console.log('輸入不合法,請輸入一個大於 0 的整數'); } finally { rl.close(); } } main(); parseInt(num, 10) 會把使用者輸入的字串轉成整數或 NaN。divideBy3Times 內部用 Number.isInteger(n) || n <= 0 檢查輸入是否合法,不合法就 throw new Error(...)。try...catch 捕捉這個錯誤,用 CLI 顯示友善訊息,而不是讓未捕捉例外把 Node 行程炸掉。finally 保證 rl.close() 一定會被呼叫,避免 readline 卡住程式。 ## Publication Information - [雞蛋糕的前端修煉屋](https://paragraph.com/@gcake/): Publication homepage - [All Posts](https://paragraph.com/@gcake/): More posts from this publication - [RSS Feed](https://api.paragraph.com/blogs/rss/@gcake): Subscribe to updates