天天看點

第四節:命名空間

當系統大了、程式複雜了、寫的人多了,名字的問題就是個大問題。目前來說,解決名字問題的最好辦法就是命名空間。

使用命名空間,可以避免變量或對象名稱産生的沖突,同時,也有助于組織代碼,有更強的可維護性和可讀性。

javascript不提供原生的命名空間支援,但我們可以利用javascript的一些語言特性,實作類似的效果。

1.閉包

簡單的說,閉包就是,一個函數可以使用函數之外定義的變量,那麼這個函數就是閉包。

var msg = "你好,朋友";

function sayhello() {

  alert(msg);

}

sayhello();

這就是一個最簡單的閉包執行個體。函數sayhello調用了函數外部定義的變量msg,是以函數sayhello就是閉包。

function extfunc() {

    var n = 1;

    function internal_func(){

        alert(n++);

    }

    return internal_func();

extfunc();

這個例子稍微複雜一些,其内部函數(internal_func )是一個閉包,因為它使用外部函數的變量n的值。extfunc()的最後一步調用了internal_func(),增加了變量n的值,并列印它。

通過閉包可以讓函數内部變量的值始終保持在記憶體中。比如下面的例子,函數fn()無論執行多少次,其内部變量n的值都是1,因為函數每次執行的時候都會重新配置設定記憶體,并初始化其内部變量,而且執行完畢,會釋放函數内部變量的記憶體。

function fn() {

    alert(n++);

fn();

現在,我們用閉包做一些處理。

    function in_fn(){

    return in_fn;

var f = fn();

f();

我們發現,每次執行,函數fn()内部的變量n的值都會加一,說明變量n的值始終儲存在記憶體中了。

這個例子中,函數fn()最後傳回了閉包函數in_fn(),也就是說var f=fn()執行後,f其實是函數in_fn()的引用,此時,函數執行個體fn()在f引用銷毀之前不會銷毀,是以,其内部變量n可以儲存在記憶體中。

2.自調用匿名函數

自調用匿名函數,或者說,立即執行函數表達式,英文縮寫iife,是一個立即執行的匿名函數表達式,我們把這種代碼結構稱為“自調用匿名函數”。

(function() {

    alert("我是一個自調用匿名函數");

})();

該代碼定義了一個匿名函數,而且會立即執行。其實,這段代碼等同于下面的代碼,但這種表達方式更加簡單直接。

function fn(){

};

下面的方式也是合法的。

(function(){

}());

當然,自調用匿名函數是可以帶參數的。

(function(window){

    window.msg = "我被自調用匿名函數初始化";

})(window);

alert(window.msg);

第四節:命名空間

這裡,自調用匿名函數執行的時候,将window對象傳給了它,它給window對象設定了一個屬性msg,并初始化。

很明顯的是,對于自調用匿名函數,我們不能調用兩次,是以,它非常适合做一次性或者初始化性質的工作。

要特别注意的是,自調用匿名函數用在指派函數表達式中的情況。先看下面的例子。

var fn = function(){

    return "這是函數執行結果";

alert(fn);

其fn是一個指派函數,我們列印出來的是函數的代碼:

第四節:命名空間

}();

這時候fn就不是一個函數的位址了,而是匿名函數執行後的結果:

第四節:命名空間

3.命名空間

是以,我們可以通過函數作用域來實作javascrpt的命名空間 ,也就是利用自調用匿名函數,建立一個特殊的函數作用域,将該作用域中的代碼和外界隔離開,進而實作命名空間的概念。

比如jquery架構的命名空間:

(function(window,undefined){

     var jquery = (function(){

          var jquery = function(){

               alert("建立jquery對象");

          };

          return jquery;

     })();

     window.jquery = window.$ = jquery;

執行$(),或者jquery(),顯示:

第四節:命名空間

該代碼通過自調用匿名函數建立了一個和外界隔離的對象jquery,并通過全局變量$(window.$)和jquery(window.jquery)對外公開,其jquery對象起到了命名空間的作用。同時利用閉包的特性,讓命名空間對象的值始終保持在記憶體中。

4.子產品化

雖然我們通過javascript的閉包、自調用匿名函數等特性,建立了命名空間,或者說我們建立了命名空間模式,可以很好地解決名字問題。但随着系統的增大,需要團隊協作完成一個項目,此時,多檔案就是必須的了。多檔案開發環境下,子產品化程式設計就是我們必須要掌握的一項技術。

我們可以通過擴充模式,來實作多人、多檔案開發。比如,a定義了一個命名空間module_a,為了表述的統一,這裡我們稱為子產品module_a。

    var module = {};

    module.moduleproperty = "子產品module_a的原始屬性moduleproperty";

    module.modulemethod = function () {

        alert("子產品module_a的原始方法modulemethod");

    };

    window.module_a = module;

該代碼寫在檔案file_a.js中。現在,b要負責module_a子產品其他方法的實作,他可以在檔案file_b.js中,通過擴充模式,給module_a添加方法、或者屬性。

(function(module){

    module.anothermethod = function () {

            alert("子產品module_a的擴充方法anothermethod");

        };

})(window.module_a);

下面,我們用一個測試檔案測試一下:

<!doctype html>

<html lang="en">

 <head>

  <meta charset="utf-8">

  <script src="file_a.js"></script>

  <script src="file_b.js"></script>

  <title>document</title>

 </head>

 <body>

<script type="text/javascript">

module_a.modulemethod();

module_a.anothermethod();

</script>

 </body>

</html>

很好,運作順利:

第四節:命名空間
第四節:命名空間

但是,如果我們對file_a.js和file_b.js兩個檔案的引用順序錯了的話,就會産生錯誤。

第四節:命名空間
第四節:命名空間

錯誤的原因是我們在擴充module_a時,module_a還沒有定義,也就是還不存在。這個問題可以通過寬模式擴充解決。

file_a.js檔案代碼的修改:

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

file_b.js檔案代碼的修改:

        alert("子產品module_a的擴充方法anothermethod");

也就是說,我們在給自調用匿名函數傳遞參數的時候,就判斷全局變量是否存在,如果不存在則直接建立一個object對象給它。這樣,我們就可以可以建立靈活多變的子產品,可以将他們無順序加載,以寬松的方式擴充。