原文連接: Namespacing in JavaScript
譯文原鏈: 【譯】JavaScript 中的命名空間javascript
全局變量應該由有系統範圍相關性的對象們保留,而且它們的命名應該避免含糊並儘可能減小命名衝突的風險。在實踐中,這意味着你應該避免建立全局對象,除非它們是絕對必須的。java
不過,恩,這些你早都知道了……web
因此你對此是怎麼作的?傳統方法告訴咱們,最好的消除全局策略是建立少數做爲潛在模塊和子系統的實際命名空間的全局對象。我將探索幾種有關命名空間的方式,並以我基於 James Edwards 最近的一篇文章獲得的一個優雅、安全和靈活的解決方案結束。安全
我用靜態命名空間
做爲那些命名空間標籤實際上硬編碼的解決方案的涵蓋性術語。是的,你能夠將一個命名空間從新分配給另外一個,不過新的命名空間將會引用和舊的那一個一樣的對象。閉包
最基礎的方法。這樣很是冗長,而且若是你還想重命名這些命名空間,你就有得活兒幹了。不過它是安全和清楚明白的。app
var myApp = {} myApp.id = 0; myApp.next = function() { return myApp.id++; } myApp.reset = function() { myApp.id = 0; } window.console && console.log( myApp.next(), myApp.next(), myApp.reset(), myApp.next() ); //0, 1, undefined, 0
你也能夠經過使用this
引用兄弟屬性來使未來的維護更輕鬆一些,不過這有一點冒險由於沒有什麼能阻止你的那些命名空間裏的方法被從新分配。wordpress
var myApp = {} myApp.id = 0; myApp.next = function() { return this.id++; } myApp.reset = function() { this.id = 0; } myApp.next(); //0 myApp.next(); //1 var getNextId = myApp.next; getNextId(); //NaN whoops!
如今咱們只須要引用命名空間名一次,所以以後改變名字更簡單了一些(假設你還沒反覆引用這個命名空間)。仍有一個危險是this
的值可能會拋出一個『驚喜』 - 不過假設在一個對象字面結構裏定義的對象不會被從新分配相對安全一點。函數
var myApp = { id: 0, next: function() { return this.id++; }, reset: function() { this.id = 0; } } window.console && console.log( myApp.next(), myApp.next(), myApp.reset(), myApp.next() ) //0, 1, undefined, 0
我發現本身最近用模塊模式
更多。邏輯被一個方法包裝從全局域隔離開了(一般是自調用的),它返回一個表明這個模塊公開接口的對象。經過當即調用這個方法並分配結果給一個命名空間變量,咱們就鎖住了這個命名變量中模塊的 API。此外,任何沒有包括在返回值中的變量將永遠保持私有,只對引用他們的公開方法可見。工具
var myApp = (function() { var id= 0; return { next: function() { return id++; }, reset: function() { id = 0; } }; })(); window.console && console.log( myApp.next(), myApp.next(), myApp.reset(), myApp.next() ) //0, 1, undefined, 0
如上對象字面量例子,命名空間名字能夠輕易更換,不過還有額外優點:對象字面量是四班的 - 它全是關於屬性分配,沒有支持邏輯的空間。此外,全部屬性必須被初始化,而且屬性值沒法輕易跨對象引用(所以,好比,內部閉包就不可能使用了)。模塊模式沒有任何上述約束,而且給咱們額外的隱私福利。oop
咱們也能夠將這一節稱爲命名空間注入
。命名空間由一個直接引用方法包裝內部
的代理表明 - 這意味着咱們再也不須要打包分配給命名空間的返回值。這讓命名空間定義變得更靈活而且讓擁有多個存在於獨立命名空間中(或者甚至在全局上下文中)的模塊的獨立實例。動態命名空間支持模塊模式的所有特徵並附加直觀和可讀性強的優點。
在這裏咱們只是將命名空間做爲參數傳給自調用方法。變量id
是私有的,由於他並無被分配給context
。
var myApp = {}; (function(context) { var id = 0; context.next = function() { return id++; }; context.reset = function() { id = 0; } })(myApp); window.console && console.log( myApp.next(), myApp.next(), myApp.reset(), myApp.next() ) //0, 1, undefined, 0
咱們甚至能夠把context
設置給全局對象(經過一個字的改變!)。這是庫主們的巨大財富 - 他們能夠將他們的特性包裝在一個自調用函數中,而後讓用戶來決定它們是否是全局的(John Resig 在他寫 JQuery 時就是一個這個理論的早期採用者)。
var myApp = {}; (function(context) { var id = 0; context.next = function() { return id++; }; context.reset = function() { id = 0; } })(this); window.console && console.log( next(), next(), reset(), next() ) //0, 1, undefined, 0
this
做爲命名空間代理James Edwads 最近發佈的一篇文章激起了個人興趣。《My Favorite JavaScript Design Patter》 顯然被不少評論者誤解了,他們認爲他可能也是藉助於模塊模式。這篇文章宣傳了多種技術(可能致使了讀者的迷惑),可是在它的核心部分是一點我已經修改並呈現爲一個命名空間工具的很天才的東西。
這個模式的美就在於它僅僅是按照這個語言被設計的方式使用 - 很少很多、不投機也不取巧。此外由於命名空間是經過this
關鍵字(它在給定的執行上下文中是不變的)注入的,它不可能被意外修改。
var myApp = {}; (function() { var id = 0; this.next = function() { return id++; }; this.reset = function() { id = 0; } }).apply(myApp); window.console && console.log( myApp.next(), myApp.next(), myApp.reset(), myApp.next() ); //0, 1, undefined, 0
更棒的是,apply
(以及call
) API 提供了與上下文和參數自然的隔離 - 所以給模塊建立者傳遞附加參數很是乾淨。下面的例子代表了這一點,而且展現瞭如何獨立於多個命名空間來運行模塊。
var subsys1 = {}, subsys2 = {}; var nextIdMod = function(startId) { var id = startId || 0; this.next = function() { return id++; }; this.reset = function() { id = 0; } }; nextIdMod.call(subsys1); nextIdMod.call(subsys2,1000); window.console && console.log( subsys1.next(), subsys1.next(), subsys2.next(), subsys1.reset(), subsys2.next(), subsys1.next() ) //0, 1, 1000, undefined, 1001, 0
固然若是咱們若是咱們須要一個全局 id 生成器,很是簡單……
nextIdMod(); window.console && console.log( next(), next(), reset(), next() ) //0, 1, undefined, 0
這個咱們做爲例子使用的 id 生成器工具並無表現出這個模式的所有潛力。經過包裹一整個庫和使用this
關鍵字做爲命名空間的替身,咱們使得用戶在任何他們選擇的上下文中運行這個庫很輕鬆(包括全局上下文)。
//library code var protoQueryMooJo = function() { //everything } //user code var thirdParty = {}; protoQueryMooJo.apply(thirdParty);
我但願避免命名空間嵌套。它們很難追蹤(對人和電腦都是)而且它們會讓你的代碼由於一些亂七八糟的東西變得不少。如 Peter Michaux 指出的,深度嵌套的命名空間多是那些視圖從新建立他們熟悉和熱愛的長包鏈的老派 Java 開發者的遺產。
經過 .js 文件來固定一個單獨的命名空間也是能夠的(雖然只能經過命名空間注入或者直接分配每個變量),不過你應該對依賴謹慎些。此外將命名空間綁定到文件上能夠幫助讀者更輕易弄清整個代碼。
由於 JavaScript 並無正式的命名空間結構,因此有不少天然造成的方法。這個調查只詳細說明了其中的一部分,可能有更好的技術我沒有發現。我很樂意知道它們。
James Edwards: My Favorite JavaScript Design Pattern
Peter Michaux: JavaScript Namespacing