
JavaScript 語言核心(19)名稱空間管理
JavaScript 語言核心(18)模擬類別的封裝與繼承?<< 前情 你可以在 Google Play 或 Pubu 購買 JavaScript 語言核心系列文章的電子書。 JavaScript 本身沒有名稱空間管理的機制(ECMAScript 6 才有規範模組語法),名稱都是物件上的特性,要不就成為全域物件上的特性(全域變數),要不就使用 名稱衝突的問題極容易在 JavaScript 中發生,就算是在同一個 .js 檔案中也有可能發生。例如你也許寫了個 function validate() { //.. 作些驗證 } // 很長很長的程式。。 // 某年某月的某一天。。 function validate() { //.. 作些別的驗證 } 很不幸地,之後的 最基本的名稱空間管理,就是將這個函式作為某物件的特性,該物件是對組織或單位有意義的名稱所參考。例如: var openhome = {}; // 作為名稱空間 function validate() { //.. 作些驗證 } openhome.validate = validate; 想要取用你定義的 openhome.validate(); 其他人在定義函式時,也可以作類似考量。例如他也許在同一個 .js 中如下定義: var caterpillar = {}; // 作為名稱空間 function validate() { //.. 作些驗證 } caterpillar.validate = validate; 呼叫時就使用: caterpillar.validate(); 如此就不會發生名稱覆蓋的問題。或許在同一個 .js 中,以上名稱衝突的情況比較算少數,通常這會發生在兩個 .js 分別由兩個不同組識或作者撰寫時。 如果同一個組識在撰寫程式庫時,會使用同一名稱作為名稱空間,如果你在 a.js 中如下撰寫: var openhome = {}; // 作為名稱空間 function validate() { //.. 作些驗證 } openhome.validate = validate; 另一個人在b.js中如下撰寫: var openhome = {}; // 作為名稱空間 function format() { //.. 作些格式化 } openhome.format = format; 那在同一個網頁中引用 a.js 與 b.js 會如何?如果 b.js 較後引用,那麼,就沒辦法使用 var openhome = openhome || {}; function validate() { //.. 作些驗證 } openhome.validate = validate; 如上,只有在 在撰寫 JavaScript 時,要儘量避免使用全域變數,上面的例子似乎沒有?有的!a.js 中至少侵入了兩個,一個是 var openhome = openhome || {}; openhome.validate = function() { // 作些驗證 }; 但如果這個函式也想作為其他物件上的特性,函式實字的作法會比較不方便。有個方式可以比較漂亮地解決。例如: var openhome = openhome || {}; (function() { function validate() { // 作些驗證 } openhome.validate = validate; // ... 其他... })(); 乍看有些複雜,事實上,首先是撰寫.. function() { } 這寫下了一個函式實字,沒有名稱參考至它,所以不會污染全域名稱空間…接著… (function() { }) 加上括號表示優先運算子,將括號中的函式實字運算為一個函式物件,接著… (function() { })(); 最後的括號表示呼叫傳回的函式物件。在這個函式物件中所建立的函式是區域的…. var openhome = openhome || {}; (function() { function validate() { // validate 是區域的 // 作些驗證 } openhome.validate = validate; // ... 其它... })(); 所以不會污染全域名稱空間,到最後,以上只會有 在設計程式庫時,若有名稱想避免別人佔用干擾,則有個慣用手法。例如,若不想被別人佔用干擾 global 這個名稱,則可以如下: (function(global) { var global.openhome = global.openhome || {}; ... })((0, eval)('this')); 也許你的匿名函式初始中,不確定會用在哪個環境中,因此想使用 檔案名稱也是會發生衝突的,檔案名稱上也可以放上與組織相關的前置名稱。例如 openhome.formutil.js、caterpillar.formutil.js 這樣的名稱,從而避免檔案名稱上的衝突。 如果作為名稱空間的物件階層很多。例如: openhome.book.web.some = 'some'; openhome.book.web.other = 'other'; openhome.book.web.another = 'another'; 這樣不僅撰寫麻煩,而且 with(openhome.book.web) { some = 'some'; other = 'other'; another = 'another'; } 但不建議,雖然省去一些打字上的麻煩,但 var web = openhome.book.web; web.some = 'some'; web.other = 'other'; web.another = 'another'; |