Ch. 3 函式 vs. 區塊範疇


源自函式的範疇

JS 具有以函式為基礎的範疇。其內的識別字只能在函式的範疇內被取用。


隱藏在普通範疇中

函式的相反思維:當把程式碼或變數放在函式中,其效用等於「隱藏」了程式碼。

最小權限原則 (principle of least privilege) / 最小授權 (least authority) / 最小暴露 (least exposure):在模組或物件 API 中,應該只暴露最少的東西。

參考 P.31 程式碼,私有變數或函式等細節,應被隱藏起來,以避免非預期的使用。

你會隱藏嗎?或是覺得什麼情況可以隱藏,請舉出一些例子。


避免衝突

隱藏可以避免同名但用途不同的識別字產生衝突或複寫。

全域命名空間

載入的程式庫如果沒有隱藏私有函式、變數,容易汙染全域空間,彼此發生衝突。

可以建立一個物件作為命名空間來使用,將對外的函式或變數設定為該物件的屬性。

模組管理

另一個方法是使用模組 (module),藉由工具產生相依性管理,不新增識別字到全域範疇。

模組管理機制並沒有逃離範疇規則的掌控,而是利用規則將識別字放置於私有的、不易發生衝突的範疇中。


函式作為範疇

包在函式中的確能夠隱藏裡面的細節,但有一些問題:

  • 必須宣告一個函式名稱,如 foo
  • 要明確地呼叫函式才能夠執行。

JS 針對這兩個問題提供了解法:IIFE。

函式宣告 vs. 函式運算式

函式宣告:利用 function 為關鍵字來宣告。

函式運算式:將函式指定給特定變數,如 var foo = function () { ... }(function foo() { ... })();

(function foo() { ... }) 代表 foo 只能在 … 所標示的範疇中所找到,不會汙染包含它的範疇。

考慮以下兩個函式運算式。

var foo = function(){
	console.log(foo);
}
(function foo(){
	console.log(foo);
})()

匿名 vs. 具名

函式運算式可以是匿名的。缺點:

  • 除錯困難:在堆疊中沒有名稱。
  • 要使用時可能被棄用:需要參考或遞迴時,或是要解除事件繫結 (unbind event) 的時候。
  • 可讀性降低:可識別的名稱能讓程式碼較容易理解。

最佳的實務做法是永遠讓函式運算式帶有自己的名稱。

即刻調用函式運算式 (IIFE)

IIFE 是可立即執行的函式運算式,不會汙染到全域範圍,並且匿名或具名皆合法。

各種變體 (P.37):

  • 將 window 重新指名為 global。
  • 確保 undefined 正確性。
  • 反轉順序,不傳變數而是傳函式。

區塊作為範疇

盡可能在最近的地方宣告變數。

for (var i = 0; i<10; i++) {
	console.log(i);
}
console.log(i);

例如預期 i 只在 for 中被使用,但這並不是真正的區塊範疇,而是仰賴風格和自我約束偽造出來的範疇。

with

with 透過物件建立出來的範疇,實際上是一種區塊範疇的實例。這個範疇只會在 with 的生命週期中存在,而且不在包含它的範疇中。

try/catch

在 try/catch 中的變數宣告,會以 catch 區塊為範疇。

let

ES6 改變世界~

let 會隱含地將變數宣告接附到它所在的區塊 (通常是一對 { … })。

可以在程式碼中放入明確的一對 { … } ,以製作一個明確的區塊。

let 不會被拉升。

垃圾回收

宣告明確的區塊,可以向 Engine 清楚地表示範圍中的識別字不需要再被保留了。(參考 P.43)

let 迴圈

for (let i = 0; i < 10; i++) {
	console.log(i);
}
console.log(i); // ReferenceError

在 for 迴圈中使用,不只將 i 限制於 for 的區塊範疇中,還會每次都重新綁定 (rebind),將前一次迭代的結果重新賦值。

如果現有程式碼對變數宣告有範疇依賴性,重構或移動時要特別注意 (參考 P.45)。

const

與 let 雷同,但其值是固定不可變的。嘗試改變會有 ReferenceError。


複習

  • 宣告在函式內的識別字會被隱藏起來。
  • 除了函式範疇,也有區塊範疇 (ES3 catch、ES6 let, const)。

附錄 B

使用 try/catch 來達成區塊範疇實作。

let 述句,透過 let 述句建立一個明確的區塊,並將宣告至於區塊的頂端。


發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *