好程式設計師分享JavaScript名稱空間模式例項詳解

好程式設計師IT發表於2019-07-23

   好程式設計師分享 JavaScript 名稱空間模式例項詳解, 本文例項講述了 JavaScript名稱空間模式 具體如下:

   名稱空間可以被認為是唯一識別符號下程式碼的邏輯分組。為什麼會出現名稱空間這一概念呢?因為可用的單詞數太少,並且不同的人寫的程式不可能所有的變數都沒有重名現象。在 JavaScript中,名稱空間可以幫助我們防止與全域性名稱空間下的其他物件或變數產生衝突。名稱空間也有助於組織程式碼,有更強的可維護性和可讀性。本文旨在探討JavaScript裡的幾種常見名稱空間模式,為我們提供一個思路。

   JavaScript執行環境有很多獨特之處,全域性變數和函式的使用就是其中之一。JavaScript的執行環境由各種各樣的全域性變數構成,這些全域性變數先於函式執行環境而建立。這些全域性變數都掛載於“全域性物件”下,在瀏覽器中,window物件就等同於全域性物件。那麼,在全域性作用域中宣告的任何變數和函式都是window物件的屬性,當名稱有衝突時,就會產生一些不可控的問題。全域性變數會帶來以下問題:

命名衝突

程式碼的脆弱性

難以測試

在程式設計開發中合理的使用名稱空間,可以避免相同的變數或物件名稱產生的衝突。而且,名稱空間也有助於組織程式碼,有更強的可維護性和可讀性。 JavaScript中雖然沒有提供原生的名稱空間支援,但我們可以使用其他的方法(物件和閉包)實現類似的效果。下面就是一些常見的名稱空間模式:

1.單一全域性變數

JavaScript中一個流行的名稱空間模式是選擇一個全域性變數作為主要的引用物件。因為每個可能的全域性變數都成為唯一全域性變數的屬性,也就不用再建立多個全域性變數,那麼也就避免了和其他宣告的衝突。

單一全域性變數模式已經在不少的 JavaScript類庫中使用,如:

  • YUI定義了唯一的YUI全域性物件
  • jQuery定義了和jQuery,和jQuery,由其他類庫使用時使用jQuery
  • Dojo定義了一個Dojo全域性變數
  • Closure類庫定義了一個goog全域性物件
  • Underscore類庫定義了一個_ 全域性物件

示例如下:

var myApplication = (function() {

  function() {

    // ***

  },

  return {

    // **

  }

})();

雖然單一全域性變數模式適合某些情況,但其最大的挑戰是確保單一全域性變數在頁面中是唯一使用的,不會發生命名衝突。

2.名稱空間字首

名稱空間字首模式其思路非常清晰,就是選擇一個獨特的名稱空間,然後在其後面宣告宣告變數、方法和物件。示例如下:

var = myApplication_propertyA = {};

var = myApplication_propertyA = {};

function myApplication_myMethod() {

  // ***

}

從某種程度上來說,它確實減少了命名衝突的發生機率,但其並沒有減少全域性變數的數目。當應用程式規模擴大時,就會產生很多的全域性變數。在全域性名稱空間內,這種模式對其他人都沒有使用的這個字首有很強的依賴,而且有些時候也不好判斷是否有人已經使用某個特殊字首,在使用這種模式時一定要特別注意。

3.物件字面量表示法

物件字面量模式可以認為是包含一組鍵值對的物件,每一對鍵和值由冒號分隔,鍵也可以是程式碼新的名稱空間。示例如下:

var myApplication = {

  // 可以很容易的為物件字面量定義功能

  getInfo:function() {

    // ***

  },

  // 可以進一步支撐物件名稱空間

  models:{},

  views:{

    pages:{}

  },

  collections:{}

};

與為物件新增屬性一樣,我們也可以直接將屬性新增到名稱空間。物件字面量方法不會汙染全域性名稱空間,並在邏輯上協助組織程式碼和引數。並且,這種方式可讀性和可維護性非常強,當然我們在使用時應當進行同名變數的存在性測試,以此來避免衝突。下面是一些常用的檢測方法:

var myApplication = myApplication || {};

if(!myApplication) {

  myApplication = {};

}

window.myApplication || (window.myApplication || {});

// 針對jQuery

var myApplication = $.fn.myApplication = function() {};

var myApplication = myApplication === undefined ? {} :myApplication;

 

物件字面量為我們提供了優雅的鍵 /值語法,我們可以非常便捷的組織程式碼,封裝不同的邏輯或功能,而且可讀性、可維護性、可擴充套件性極強。

4.巢狀名稱空間

巢狀名稱空間模式可以說是物件字面量模式的升級版,它也是一種有效的避免衝突模式,因為即使一個名稱空間存在,它也不太可能擁有同樣的巢狀子物件。示例如下:

var myApplication = myApplication || {};

// 定義巢狀子物件

myApplication.routers = myApplication.routers || {};

myApplication.routers.test = myApplication.routers.test || {};

當然,我們也可以選擇宣告新的巢狀名稱空間或屬性作為索引屬性,如:

myApplication['routers'] = myApplication['routers'] || {};

使用巢狀名稱空間模式,可以使程式碼易讀且有組織性,而且相對安全,不易產生衝突。其弱點是,如果我們的名稱空間巢狀過多,會增加瀏覽器的查詢工作量,我們可以把要多次訪問的子物件進行區域性快取,以此來減少查詢時間。

5.立即呼叫的函式表示式

立即呼叫函式( IIFE)實際上就是匿名函式,被定義後立即被呼叫。在JavaScript中,由於變數和函式都是在這樣一個只能在內部進行訪問的上下文中被顯式地定義,函式呼叫提供了一種實現私有變數和方法的便捷方式。IIFE是用於封裝應用程式邏輯的常用方法,以保護它免受全域性名稱空間的影響,其在名稱空間方面也可以發揮其特殊的作用。示例如下:

// 名稱空間和undefined作為引數傳遞,確保:

// 1.名稱空間可以在區域性修改,不重寫函式外部上下文

// 2.undefined 的引數值是確保undefined,避免ES5規範裡定義的undefined

(function (namespace, undefined) {

// 私有屬性

var foo = "foo";

  bar = "bar";

// 公有方法和屬性

namespace.foobar = "foobar";

namespace.sayHello = function () {

  say("Hello World!");

};

// 私有方法

function say(str) {

  console.log("You said:" + str);

};

})(window.namespace = window.namespace || {});

可擴充套件性是任何可伸縮名稱空間模式的關鍵,使用 IIFE可以輕鬆實現這一目的,我們可以再次使用IIFE給名稱空間新增更多的功能。

6.名稱空間注入

名稱空間注入是 IIFE的另一個變體,從函式包裝器內部為一個特定的名稱空間“注入”方法和屬性,使用this作為名稱空間代理。這種模式的優點是可以將功能行為應用到多個物件或名稱空間。示例如下:


var myApplication = myApplication || {};

myApplication.utils = {};

(function () {

  var value = 5;

  this.getValue = function () {

    return value;

  }

  // 定義新的子名稱空間

  this.tools = {};

}).apply(myApplication.utils);

(function () {

  this.diagnose = function () {

    return "diagnose";

  }

}).apply(myApplication.utils.tools);

// 同樣的方式在普通的IIFE上擴充套件功能,僅僅將上下文作為引數傳遞並修改,而不是僅僅使用this

還有一種使用 API來實現上下文和引數自然分離的方法,該模式感覺更像是一個模組的建立者,但作為模組,它還提供了一個封裝解決方案。示例如下:


var ns = ns || {},

  ns1 = ns1 || {};

// 模組、名稱空間建立者

var creator = function (val) {

  var val = val || 0;

  this.next = function () {

    return val ++ ;

  };

  this.reset = function () {

    val = 0;

  }

}

creator.call(ns);

// ns.next, ns.reset 此時已經存在

creator.call(ns1, 5000);

// ns1包含相同的方法,但值被重寫為5000了

名稱空間注入是用於為多個模組或名稱空間指定一個類似的功能基本集,但最好是在宣告私有變數或者方法時再使用它,其他時候使用巢狀名稱空間已經足以滿足需要了。

7.自動巢狀的名稱空間

巢狀名稱空間模式可以為程式碼單元提供有組織的結構層級,但每次建立一個層級時,我們也得確保其有相應的父層級。當層級數量很大時,會給我們帶來很大的麻煩,我們不能快速便捷的建立想建立的層級。那麼如何解決這個問題呢? Stoyan Stefanov提出,建立一個方法,其接收字串引數作為一個巢狀,解析它,並自動用所需的物件填充基本名稱空間。下面是這種模式的一種實現:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

function extend(ns, nsStr) {

  var parts = nsStr.split("."),

    parent = ns,

    pl;

  pl = parts.length;

  for (var i = 0; i < pl; i++) {

    // 屬性如果不存在,則建立它

    if (typeof parent[parts[i]] === "undefined") {

      parent[prats[i]] = {};

    }

    parent = parent[parts[i]];

  }

  return parent;

}

// 用法

var myApplication = myApplication || {};

var mod = extend(myApplication, "module.module2");


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/69913892/viewspace-2651492/,如需轉載,請註明出處,否則將追究法律責任。

相關文章