# Day 50:JavaScript 執行機制與資料處理設計 **Published by:** [雞蛋糕的前端修煉屋](https://paragraph.com/@gcake/) **Published on:** 2026-01-30 **URL:** https://paragraph.com/@gcake/day-50 ## Content (Day 46-49 家庭旅遊休假)JavaScript 執行機制核心概念變數提升(Hoisting)的三種行為模式變數提升是 JavaScript 在執行前會將宣告「移到作用域頂端」的行為,但不同宣告方式有不同的提升規則: developer.mozilla var 宣告的變數只提升宣告,初始化為 undefined賦值不提升,留在原本位置function declaration整個函式都提升(名稱與內容都可用)可以在宣告前呼叫function expression只提升變數名,值為 undefined函式本體不提升,宣告前呼叫會報錯// var 提升範例 console.log(a); // undefined(宣告已提升,但賦值未執行) a(); // TypeError: a is not a function var a = function() { console.log('Hello'); }; // function declaration 提升範例 greet(); // "Hi"(整個函式都提升,可以正常呼叫) function greet() { console.log('Hi'); } 具名函式表達式的特殊作用域具名函式表達式中的名稱只在函式內部有效,外部無法直接呼叫: developer.mozillaconst factorial = function calc(n) { if (n <= 1) return 1; return n * calc(n - 1); // 內部可用 calc 進行遞迴 }; factorial(5); // 120(外部用 factorial 呼叫) calc(5); // ReferenceError: calc is not defined 使用情境:遞迴函式需要呼叫自己除錯時方便辨識 stack trace作用域與變數遮蔽作用域鏈查找規則函式內若找不到變數,會沿著作用域鏈往外層尋找,這個機制稱為詞法作用域(Lexical Scope): developer.mozillavar b = 1; function outer() { console.log(b); // undefined(內層 var b 提升,遮蔽外層) var b = 2; // 內層宣告同名變數,產生遮蔽效果 } outer(); console.log(b); // 1(全域 b 不受影響) ⚠ 易錯提醒:內層宣告的變數會遮蔽外層同名變數(shadowing)var 的提升行為可能導致非預期的 undefinedlet/const/var 差異對照特性varletconst重複宣告✅ 可以❌ 不可❌ 不可重新賦值✅ 可以✅ 可以❌ 不可作用域function scopeblock scopeblock scope提升行為提升並初始化為 undefined提升但不初始化(TDZ)提升但不初始化(TDZ)宣告前存取undefinedReferenceErrorReferenceErrordeveloper.mozillaconst 的記憶體綁定特性const 固定的是變數與記憶體位址的綁定,而非記憶體內的值: developer.mozillaconst user = { name: 'Alice' }; user.name = 'Bob'; // ✅ 允許,修改物件內容 user = { name: 'Charlie' }; // ❌ TypeError,不能換綁定的物件 const numbers = [1, 2, 3]; numbers.push(4); // ✅ 允許,修改陣列內容 numbers = [5, 6]; // ❌ TypeError,不能換綁定的陣列 記憶法:const 固定「這條變數線連到哪個盒子」盒子裡的東西可以換,但不能換盒子暫時死區(TDZ)TDZ 是指從區塊開始到 let/const 宣告之間的時期,這段時間內變數無法存取: geeksforgeeksconsole.log(typeof x); // "undefined"(未宣告變數) console.log(typeof y); // ReferenceError(在 TDZ 中) let y = 10; TDZ 運作流程: geeksforgeeks變數宣告被提升但不初始化從區塊開始到宣告語句前都是 TDZ在 TDZ 中存取變數會拋出 ReferenceError執行到宣告語句後,變數才能正常使用資料處理設計模式逐步累加 vs 一次計算處理複雜規則時,逐步累加比公式計算更直觀且易維護:// 計算健身房月費(每滿5期折抵200元,首月7.9折) function calculateTotal(period) { let total = 0; let month = 1; while (month <= period) { if (month === 1) { total += 500 * 0.79; // 首月折扣 } else { total += 500; if ((month - 1) % 5 === 0) { total -= 200; // 每滿5期折抵 } } month++; } return total; } 適用情境:規則多變且依賴每期狀態需要追蹤每期細節(如優惠計算)日後可能新增特殊月份規則遞迴 vs 迴圈:避免 Stack OverflowJavaScript 遞迴有最大呼叫堆疊限制(通常數千層),處理大量資料時建議改用迴圈: youtube// ❌ 遞迴版本:資料量大時可能 stack overflow function simulateLoadingRecursive(equipments, maxLoad, current = 0, list = []) { const randomIndex = Math.floor(Math.random() * equipments.length); const selected = equipments[randomIndex]; if (current + selected.weight > maxLoad) { return { equipmentsList: list, currentLoad: current }; } list.push(selected); return simulateLoadingRecursive(equipments, maxLoad, current + selected.weight, list); } // ✅ 迴圈版本:穩定且效能更好 function simulateLoading(equipments, maxLoad) { let currentLoad = 0; const equipmentsList = []; while (true) { const randomIndex = Math.floor(Math.random() * equipments.length); const selected = equipments[randomIndex]; if (currentLoad + selected.weight > maxLoad) break; equipmentsList.push(selected); currentLoad += selected.weight; } return { equipmentsList, currentLoad }; } 遞迴使用時機: youtube資料結構天然有樹狀/巢狀關係(如 DOM 遍歷)問題本質是分治法(divide and conquer)確保有明確的終止條件且深度可控toFixed 的隱藏陷阱toFixed() 會自動四捨五入,若需精確控制小數位數,應使用數學運算:const num = 1.239; // ❌ toFixed 會四捨五入 console.log(num.toFixed(2)); // "1.24" // ✅ 用數學運算精確取位 const secondDecimal = Math.floor(num * 100) % 10; // 3 const thirdDecimal = Math.floor(num * 1000) % 10; // 9 取小數位數技巧:乘以 10 的 n 次方用 Math.floor() 取整數部分對 10 取餘數,得到第 n 位數字資料結構自動轉換將業務格式資料轉換成計算友善的扁平結構,提升開發效率:// 原始資料格式(業務規格) const rawData = [ { name: 'Kettlebell', spec: ['4 kg', '6 kg', '8 kg'], weight: [4, 6, 8] } ]; // 轉換成計算友善的扁平格式 const flatData = rawData.flatMap(item => item.spec.map((spec, index) => ({ name: item.name, spec, weight: item.weight[index] })) ); // 結果: [ // { name: 'Kettlebell', spec: '4 kg', weight: 4 }, // { name: 'Kettlebell', spec: '6 kg', weight: 6 }, // { name: 'Kettlebell', spec: '8 kg', weight: 8 } // ] 實務價值:減少手動前置處理工作更貼近真實專案的資料流設計程式碼更易測試與維護charCodeAt 查表技巧處理字母轉換時,用 UTF-16 編碼查表比 indexOf 更高效:const A_CODE = 'A'.charCodeAt(0); // 65 const Z_CODE = 'Z'.charCodeAt(0); // 90 function caesarCipher(char, shift) { const code = char.charCodeAt(0); if (code >= A_CODE && code <= Z_CODE) { // 計算偏移後的位置 const offset = (code - A_CODE + shift) % 26; return String.fromCharCode(A_CODE + offset); } return char; } caesarCipher('C', 3); // "F" 適用情境:字母表查表與轉換加密演算法實作字母排序與比對學習資源MDN: Hoisting developer.mozillaMDN: let 宣告 developer.mozillaMDN: const 宣告 developer.mozillaMDN: 遞迴 developer.mozillaTemporal Dead Zone 深入解析 geeksforgeeks ## 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