在线观看不卡亚洲电影_亚洲妓女99综合网_91青青青亚洲娱乐在线观看_日韩无码高清综合久久

鍍金池/ 教程/ HTML/ 全面解析 Module 模式
代碼復用模式(避免篇)
S.O.L.I.D 五大原則之接口隔離原則 ISP
設計模式之狀態(tài)模式
JavaScript 核心(晉級高手必讀篇)
設計模式之建造者模式
JavaScript 與 DOM(上)——也適用于新手
設計模式之中介者模式
設計模式之裝飾者模式
設計模式之模板方法
設計模式之外觀模式
強大的原型和原型鏈
設計模式之構造函數(shù)模式
揭秘命名函數(shù)表達式
深入理解J avaScript 系列(結局篇)
執(zhí)行上下文(Execution Contexts)
函數(shù)(Functions)
《你真懂 JavaScript 嗎?》答案詳解
設計模式之適配器模式
設計模式之組合模式
設計模式之命令模式
S.O.L.I.D 五大原則之單一職責 SRP
編寫高質量 JavaScript 代碼的基本要點
求值策略
閉包(Closures)
對象創(chuàng)建模式(上篇)
This? Yes,this!
設計模式之代理模式
變量對象(Variable Object)
S.O.L.I.D 五大原則之里氏替換原則 LSP
面向對象編程之一般理論
設計模式之單例模式
Function 模式(上篇)
S.O.L.I.D 五大原則之依賴倒置原則 DIP
設計模式之迭代器模式
立即調用的函數(shù)表達式
設計模式之享元模式
設計模式之原型模式
根本沒有“JSON 對象”這回事!
JavaScript 與 DOM(下)
面向對象編程之 ECMAScript 實現(xiàn)
全面解析 Module 模式
對象創(chuàng)建模式(下篇)
設計模式之職責鏈模式
S.O.L.I.D 五大原則之開閉原則 OCP
設計模式之橋接模式
設計模式之策略模式
設計模式之觀察者模式
代碼復用模式(推薦篇)
作用域鏈(Scope Chain)
Function 模式(下篇)
設計模式之工廠模式

全面解析 Module 模式

簡介

Module 模式是 JavaScript 編程中一個非常通用的模式,一般情況下,大家都知道基本用法,本文嘗試著給大家更多該模式的高級使用方式。

首先我們來看看 Module 模式的基本特征:

  1. 模塊化,可重用
  2. 封裝了變量和 function,和全局的 namaspace 不接觸,松耦合
  3. 只暴露可用 public 的方法,其它私有方法全部隱藏

關于 Module 模式,最早是由 YUI 的成員 Eric Miraglia 在 4 年前提出了這個概念,我們將從一個簡單的例子來解釋一下基本的用法(如果你已經非常熟悉了,請忽略這一節(jié))。

基本用法

先看一下最簡單的一個實現(xiàn),代碼如下:

var Calculator = function (eq) {
    //這里可以聲明私有成員
    var eqCtl = document.getElementById(eq);
    return {
        // 暴露公開的成員
        add: function (x, y) {
            var val = x + y;
            eqCtl.innerHTML = val;
        }
    };
};

我們可以通過如下的方式來調用:

var calculator = new Calculator('eq');
calculator.add(2, 2);

大家可能看到了,每次用的時候都要 new 一下,也就是說每個實例在內存里都是一份 copy,如果你不需要傳參數(shù)或者沒有一些特殊苛刻的要求的話,我們可以在最后一個}后面加上一個括號,來達到自執(zhí)行的目的,這樣該實例在內存中只會存在一份 copy,不過在展示他的優(yōu)點之前,我們還是先來看看這個模式的基本使用方法吧。

匿名閉包

匿名閉包是讓一切成為可能的基礎,而這也是 JavaScript 最好的特性,我們來創(chuàng)建一個最簡單的閉包函數(shù),函數(shù)內部的代碼一直存在于閉包內,在整個運行周期內,該閉包都保證了內部的代碼處于私有狀態(tài)。

(function () {
    // ... 所有的變量和function都在這里聲明,并且作用域也只能在這個匿名閉包里
    // ...但是這里的代碼依然可以訪問外部全局的對象
}());

注意,匿名函數(shù)后面的括號,這是 JavaScript 語言所要求的,因為如果你不聲明的話,JavaScript 解釋器默認是聲明一個 function 函數(shù),有括號,就是創(chuàng)建一個函數(shù)表達式,也就是自執(zhí)行,用的時候不用和上面那樣在 new 了,當然你也可以這樣來聲明:

(function () {/* 內部代碼 */})();

不過我們推薦使用第一種方式,關于函數(shù)自執(zhí)行,我后面會有專門一篇文章進行詳解,這里就不多說了。

引用全局變量

JavaScript 有一個特性叫做隱式全局變量,不管一個變量有沒有用過,JavaScript 解釋器反向遍歷作用域鏈來查找整個變量的 var 聲明,如果沒有找到 var,解釋器則假定該變量是全局變量,如果該變量用于了賦值操作的話,之前如果不存在的話,解釋器則會自動創(chuàng)建它,這就是說在匿名閉包里使用或創(chuàng)建全局變量非常容易,不過比較困難的是,代碼比較難管理,尤其是閱讀代碼的人看著很多區(qū)分哪些變量是全局的,哪些是局部的。

不過,好在匿名函數(shù)里我們可以提供一個比較簡單的替代方案,我們可以將全局變量當成一個參數(shù)傳入到匿名函數(shù)然后使用,相比隱式全局變量,它又清晰又快,我們來看一個例子:

(function ($, YAHOO) {
    // 這里,我們的代碼就可以使用全局的jQuery對象了,YAHOO也是一樣
} (jQuery, YAHOO));

現(xiàn)在很多類庫里都有這種使用方式,比如 jQuery 源碼。

不過,有時候可能不僅僅要使用全局變量,而是也想聲明全局變量,如何做呢?我們可以通過匿名函數(shù)的返回值來返回這個全局變量,這也就是一個基本的 Module 模式,來看一個完整的代碼:

var blogModule = (function () {
    var my = {}, privateName = "博客園";
    function privateAddTopic(data) {
        // 這里是內部處理代碼
    }
    my.Name = privateName;
    my.AddTopic = function (data) {
        privateAddTopic(data);
    };
    return my;
} ());

上面的代碼聲明了一個全局變量 blogModule,并且?guī)в?2 個可訪問的屬性:blogModule.AddTopic 和 blogModule.Name,除此之外,其它代碼都在匿名函數(shù)的閉包里保持著私有狀態(tài)。同時根據(jù)上面?zhèn)魅肴肿兞康睦?,我們也可以很方便地傳入其它的全局變量?/p>

高級用法

上面的內容對大多數(shù)用戶已經很足夠了,但我們還可以基于此模式延伸出更強大,易于擴展的結構,讓我們一個一個來看。

擴展

Module 模式的一個限制就是所有的代碼都要寫在一個文件,但是在一些大型項目里,將一個功能分離成多個文件是非常重要的,因為可以多人合作易于開發(fā)。再回頭看看上面的全局參數(shù)導入例子,我們能否把 blogModule 自身傳進去呢?答案是肯定的,我們先將 blogModule 傳進去,添加一個函數(shù)屬性,然后再返回就達到了我們所說的目的,上代碼:

var blogModule = (function (my) {
    my.AddPhoto = function () {
        //添加內部代碼  
    };
    return my;
} (blogModule)); 

這段代碼,看起來是不是有 C#里擴展方法的感覺?有點類似,但本質不一樣哦。同時盡管 var 不是必須的,但為了確保一致,我們再次使用了它,代碼執(zhí)行以后,blogModule 下的 AddPhoto 就可以使用了,同時匿名函數(shù)內部的代碼也依然保證了私密性和內部狀態(tài)。

松耦合擴展

上面的代碼盡管可以執(zhí)行,但是必須先聲明 blogModule,然后再執(zhí)行上面的擴展代碼,也就是說步驟不能亂,怎么解決這個問題呢?我們來回想一下,我們平時聲明變量都是這樣的:

var cnblogs = cnblogs || {} ;

這是確保 cnblogs 對象,在存在的時候直接用,不存在的時候直接賦值為{},我們來看看如何利用這個特性來實現(xiàn) Module 模式的任意加載順序:

var blogModule = (function (my) {
    // 添加一些功能       
    return my;
} (blogModule || {}));  

通過這樣的代碼,每個單獨分離的文件都保證這個結構,那么我們就可以實現(xiàn)任意順序的加載,所以,這個時候的 var 就是必須要聲明的,因為不聲明,其它文件讀取不到哦。

緊耦合擴展

雖然松耦合擴展很牛叉了,但是可能也會存在一些限制,比如你沒辦法重寫你的一些屬性或者函數(shù),也不能在初始化的時候就是用 Module 的屬性。緊耦合擴展限制了加載順序,但是提供了我們重載的機會,看如下例子:

var blogModule = (function (my) {
    var oldAddPhotoMethod = my.AddPhoto;
    my.AddPhoto = function () {
        // 重載方法,依然可通過oldAddPhotoMethod調用舊的方法
    };
    return my;
} (blogModule));

通過這種方式,我們達到了重載的目的,當然如果你想在繼續(xù)在內部使用原有的屬性,你可以調用 oldAddPhotoMethod 來用。

克隆與繼承

var blogModule = (function (old) {
    var my = {},
        key;
    for (key in old) {
        if (old.hasOwnProperty(key)) {
            my[key] = old[key];
        }
    }
    var oldAddPhotoMethod = old.AddPhoto;
    my.AddPhoto = function () {
        // 克隆以后,進行了重寫,當然也可以繼續(xù)調用oldAddPhotoMethod
    };
    return my;
} (blogModule));

這種方式靈活是靈活,但是也需要花費靈活的代價,其實該對象的屬性對象或 function 根本沒有被復制,只是對同一個對象多了一種引用而已,所以如果老對象去改變它,那克隆以后的對象所擁有的屬性或 function 函數(shù)也會被改變,解決這個問題,我們就得是用遞歸,但遞歸對 function 函數(shù)的賦值也不好用,所以我們在遞歸的時候 eval 相應的 function。不管怎么樣,我還是把這一個方式放在這個帖子里了,大家使用的時候注意一下就行了。

跨文件共享私有對象

通過上面的例子,我們知道,如果一個 module 分割到多個文件的話,每個文件需要保證一樣的結構,也就是說每個文件匿名函數(shù)里的私有對象都不能交叉訪問,那如果我們非要使用,那怎么辦呢? 我們先看一段代碼:

var blogModule = (function (my) {
    var _private = my._private = my._private || {},       
        _seal = my._seal = my._seal || function () {
            delete my._private;
            delete my._seal;
            delete my._unseal;           
        },
        _unseal = my._unseal = my._unseal || function () {
            my._private = _private;
            my._seal = _seal;
            my._unseal = _unseal;
        };      
    return my;
} (blogModule || {}));

任何文件都可以對他們的局部變量_private 設屬性,并且設置對其他的文件也立即生效。一旦這個模塊加載結束,應用會調用 blogModule._seal()"上鎖",這會阻止外部接入內部的_private。如果這個模塊需要再次增生,應用的生命周期內,任何文件都可以調用_unseal() ”開鎖”,然后再加載新文件。加載后再次調用 _seal()”上鎖”。

子模塊

最后一個也是最簡單的使用方式,那就是創(chuàng)建子模塊

blogModule.CommentSubModule = (function () {
    var my = {};
    // ...
    return my;
} ());

盡管非常簡單,我還是把它放進來了,因為我想說明的是子模塊也具有一般模塊所有的高級使用方式,也就是說你可以對任意子模塊再次使用上面的一些應用方法。

總結

上面的大部分方式都可以互相組合使用的,一般來說如果要設計系統(tǒng),可能會用到松耦合擴展,私有狀態(tài)和子模塊這樣的方式。另外,我這里沒有提到性能問題,但我認為 Module 模式效率高,代碼少,加載速度快。使用松耦合擴展允許并行加載,這更可以提升下載速度。不過初始化時間可能要慢一些,但是為了使用好的模式,這是值得的。