函式範疇的實際用途:最小暴露原則
本文延續詞彙範疇的討論,從理論進入實踐。了解範疇的運作機制之後,下一個問題是:我們可以用這個知識做什麼?這篇要介紹函式範疇最重要的應用場景,以及支撐它的軟體工程原則。
名稱衝突:一個真實的問題
javascript
var teacher = "Kyle";
// .. 某些程式碼 ..
var teacher = "Suzy"; // 後來插入的程式碼
console.log(teacher); // Suzy
// .. 更多程式碼 ..
console.log(teacher); // Suzy -- 壞掉了!這是一個在真實專案中極為常見的場景。原本第 1 行的 teacher 預期在第 10 行仍然有效,但後來插入的程式碼(可能來自不熟悉整個程式碼庫的開發者)使用了相同的變數名稱,覆蓋了原本的值。
你可能會想「改用 const 就好了」,但那只會讓後來的開發者遇到報錯,然後可能直接把 const 改成 var 繞過去。問題的根本不是變數能否被重新賦值,而是**名稱衝突(naming collision)**本身。
用函式建立新範疇
javascript
var teacher = "Kyle";
function anotherTeacher() {
var teacher = "Suzy"; // 藍色彈珠,不影響外層
console.log(teacher); // Suzy
}
anotherTeacher();
console.log(teacher); // Kyle,正確!把程式碼包在函式裡,就建立了一個新的範疇桶子。內層的 teacher 是藍色彈珠,外層的是紅色彈珠,兩者互不干擾。
但這個方法有一個新問題:anotherTeacher 這個函式名稱本身也出現在外層範疇,等於我們只是把一個名稱衝突換成了另一個名稱衝突。我們需要一種方式,能夠建立新範疇但又不在外層留下任何名稱。
最小暴露原則(Principle of Least Exposure / Least Privilege)
在解決這個問題之前,有必要先理解支撐這整個討論的軟體工程原則:
預設把所有東西設為私有,只暴露最少必要的部分。
這個原則之所以重要,是因為它解決了三個核心問題:
問題一:名稱衝突 把識別字隱藏在私有範疇中,縮小了名稱可能發生衝突的表面積。
問題二:防止誤用 一旦你公開了某個東西,就無法阻止其他人使用它——即使你明確告訴他們不要用。只有把它隱藏起來,才能真正防止外部存取。
問題三:保護重構的自由 這是三個原因中最重要、也最常被忽視的一個。當你公開了某個實作細節,就幾乎可以保證有人會開始依賴它。一旦有人依賴,你就失去了自由修改它的能力,因為修改會破壞依賴方的程式碼。把實作細節隱藏在私有範疇內,才能保留未來重構的自由。
解法:把函式呼叫拆成兩步驟
解決「函式名稱污染外層範疇」問題的關鍵,是重新思考函式呼叫的過程:
javascript
anotherTeacher();
// 等同於兩個步驟:
// 步驟一:取出 anotherTeacher 的值(函式參考)
// 步驟二:用 () 執行它
// 用括號明確分開兩個步驟:
(anotherTeacher)(); // 完全合法,結果相同把函式本身用括號包起來,在語法上仍然有效,第一組括號取出函式的值,第二組括號執行它。這個思路是後續章節要介紹的 IIFE 模式(立即調用函式表達式)的推導起點。
小結
函式範疇最重要的實際用途是隱藏實作細節,這對應到軟體工程的最小暴露原則。名稱衝突、誤用防護、重構自由,是這個原則解決的三個核心問題。目前用具名函式建立範疇仍有缺陷(函式名稱本身會污染外層範疇),解決這個問題的方式將在下一篇介紹。
複習
軟體開發中的最小暴露原則(或最小權限原則)是什麼?
這個原則建議預設將所有東西設為私有,只暴露最少必要的部分,有助於防止名稱衝突、防止誤用,並讓未來的重構更容易進行。
最小暴露原則解決的三個主要問題是什麼?
- 降低名稱衝突的風險
- 防止暴露的程式碼被意外或故意誤用
- 保護自由重構實作細節的能力,避免因依賴方存在而受到限制
當變數在同一範疇中宣告時,可能發生什麼主要問題?
名稱衝突:兩個不同的實體嘗試使用相同的語意名稱,可能互相覆蓋或衝突。
把程式碼包在函式中,如何幫助降低名稱衝突的風險?
透過為變數建立一個新的範疇,防止相同名稱的變數在原始範疇中直接發生衝突。
把函式用括號包起來再執行,會發生什麼?
函式仍然可以執行,第一組括號取出函式的值,第二組括號執行這個函式。
小測驗
哪個原則建議將大部分程式碼設為私有,只暴露最少必要的部分?
最小暴露原則最小暴露原則解決的主要問題之一是什麼?
防止名稱衝突為什麼在軟體開發中隱藏實作細節很重要?
保護未來重構的能力當程式碼的實作細節被公開暴露時,會發生什麼?
其他人很可能會開始依賴這些細節當同一範疇中的變數缺乏適當管理時,可能發生什麼?
名稱衝突問題此文章是 FrontendMasters 上的 Deep JavaScript Foundations, v3 課程筆記
