鍍金池/ 教程/ HTML/ 亨元模式
中介者模式
MVVM
亨元模式
設(shè)計(jì)模式分類概覽表
ES Harmony
組合模式
CommonJS
jQuery 插件的設(shè)計(jì)模式
外觀模式
觀察者模式
建造者模式
構(gòu)造器模式
外觀模式
簡(jiǎn)介
AMD
原型模式
設(shè)計(jì)模式的分類
觀察者模式
命名空間模式
代理模式
編寫(xiě)設(shè)計(jì)模式
適配器模式
反模式
什么是設(shè)計(jì)模式
模塊化模式
MVC
Mixin 模式
裝飾模式
設(shè)計(jì)模式的結(jié)構(gòu)
單例模式
迭代器模式
命令模式
工廠模式
MVP
暴露模塊模式
惰性初始模式

亨元模式

享元模式是一個(gè)優(yōu)化重復(fù)、緩慢和低效數(shù)據(jù)共享代碼的經(jīng)典結(jié)構(gòu)化解決方案。它的目標(biāo)是以相關(guān)對(duì)象盡可能多的共享數(shù)據(jù),來(lái)減少應(yīng)用程序中內(nèi)存的使用(例如:應(yīng)用程序的配置、狀態(tài)等)。

此模式最先由Paul Calder 和 Mark Linton在1990提出,并用拳擊等級(jí)中少于112磅體重的等級(jí)名稱來(lái)命名。享元(“Flyweight”英語(yǔ)中的輕量級(jí))的名稱本身是從以幫以助我們完成減少重量(內(nèi)存標(biāo)記)為目標(biāo)的重量等級(jí)推導(dǎo)出的。

實(shí)際應(yīng)用中,輕量級(jí)的數(shù)據(jù)共享采集被多個(gè)對(duì)象使用的相似對(duì)象或數(shù)據(jù)結(jié)構(gòu),并將這些數(shù)據(jù)放置于單個(gè)的擴(kuò)展對(duì)象中。我們可以把它傳遞給依靠這些數(shù)據(jù)的對(duì)象,而不是在他們每個(gè)上面都存儲(chǔ)一次。

使用享元

有兩種方法來(lái)使用享元。第一種是數(shù)據(jù)層,基于存儲(chǔ)在內(nèi)存中的大量相同對(duì)象的數(shù)據(jù)共享的概念。第二種是DOM層,享元模式被作為事件管理中心,以避免將事件處理程序關(guān)聯(lián)到我們需要相同行為父容器的所有子節(jié)點(diǎn)上。 享元模式通常被更多的用于數(shù)據(jù)層,我們先來(lái)看看它。

享元和數(shù)據(jù)共享

對(duì)于這個(gè)應(yīng)用程序而言,圍繞經(jīng)典的享元模式有更多需要我們意識(shí)到的概念。享元模式中有一個(gè)兩種狀態(tài)的概念——內(nèi)在和外在。內(nèi)在信息可能會(huì)被我們的對(duì)象中的內(nèi)部方法所需要,它們絕對(duì)不可以作為功能被帶出。外在信息則可以被移除或者放在外部存儲(chǔ)。

帶有相同內(nèi)在數(shù)據(jù)的對(duì)象可以被一個(gè)單獨(dú)的共享對(duì)象所代替,它通過(guò)一個(gè)工廠方法被創(chuàng)建出來(lái)。這允許我們?nèi)ワ@著降低隱式數(shù)據(jù)的存儲(chǔ)數(shù)量。

個(gè)中的好處是我們能夠留心于已經(jīng)被初始化的對(duì)象,讓只有不同于我們已經(jīng)擁有的對(duì)象的內(nèi)在狀態(tài)時(shí),新的拷貝才會(huì)被創(chuàng)建。

我們使用一個(gè)管理器來(lái)處理外在狀態(tài)。如何實(shí)現(xiàn)可以有所不同,但針對(duì)此的一種方法就是讓管理器對(duì)象包含一個(gè)存儲(chǔ)外在狀態(tài)以及它們所屬的享元對(duì)象的中心數(shù)據(jù)庫(kù)。

經(jīng)典的享元實(shí)現(xiàn)

近幾年享元模式已經(jīng)在Javascript中得到了深入的應(yīng)用,我們會(huì)用到的許多實(shí)現(xiàn)方式其靈感來(lái)自于Java和C++的世界。

我們第一個(gè)要來(lái)看的關(guān)于享元模式的代碼是我的對(duì)來(lái)自維基百科的針對(duì)享元模式的 Java 示例的 Javascript 實(shí)現(xiàn)。

在這個(gè)實(shí)現(xiàn)中我們將要使用如下所列的三種類型的享元組件:

  • 享元對(duì)應(yīng)的是一個(gè)接口,通過(guò)此接口能夠接受和控制外在狀態(tài)。
  • 構(gòu)造享元來(lái)實(shí)際的實(shí)際的實(shí)現(xiàn)接口,并存儲(chǔ)內(nèi)在狀態(tài)。構(gòu)造享元須是能夠被共享的,并且具有操作外在狀態(tài)的能力。
  • 享元工廠負(fù)責(zé)管理享元對(duì)象,并且也創(chuàng)建它們。它確保了我們的享元對(duì)象是共享的,并且可以對(duì)其作為一組對(duì)象進(jìn)行管理,這一組對(duì)象可以在我們需要的時(shí)候查詢其中的單個(gè)實(shí)體。如果一個(gè)對(duì)象已經(jīng)在一個(gè)組里面創(chuàng)建好了,那它就會(huì)返回該對(duì)象,否則它會(huì)在對(duì)象池中新創(chuàng)建一個(gè),并且返回之。

這些對(duì)應(yīng)于我們實(shí)現(xiàn)中的如下定義:

  • CoffeeOrder:享元
  • CoffeeFlavor:構(gòu)造享元
  • CoffeeOrderContext:輔助器
  • CoffeeFlavorFactory:享元工廠
  • testFlyweight:對(duì)我們享元的使用

鴨式?jīng)_減的 “implements”

鴨式?jīng)_減允許我們擴(kuò)展一種語(yǔ)言或者解決方法的能力,而不需要變更運(yùn)行時(shí)的源。由于接下的方案需要使用一個(gè)Java關(guān)鍵字“implements”來(lái)實(shí)現(xiàn)接口,而在Javascript本地看不到這種方案,那就讓我們首先來(lái)對(duì)它進(jìn)行鴨式?jīng)_減。

Function.prototype.implementsFor 在一個(gè)對(duì)象構(gòu)造器上面起作用,并且將接受一個(gè)父類(函數(shù)—)或者對(duì)象,而從繼承于普通的繼承(對(duì)于函數(shù)而言)或者虛擬繼承(對(duì)于對(duì)象而言)都可以。

// Simulate pure virtual inheritance/"implement" keyword for JS Function.prototype.implementsFor = function( parentClassOrObject ){ if ( parentClassOrObject.constructor === Function ) { // Normal Inheritance this.prototype = new parentClassOrObject(); this.prototype.constructor = this; this.prototype.parent = parentClassOrObject.prototype; } else { // Pure Virtual Inheritance this.prototype = parentClassOrObject; this.prototype.constructor = this; this.prototype.parent = parentClassOrObject; } return this; };

我們可以通過(guò)讓一個(gè)函數(shù)明確的繼承自一個(gè)接口來(lái)彌補(bǔ)implements關(guān)鍵字的缺失。下面,為了使我們得以去分配支持一個(gè)對(duì)象的這些實(shí)現(xiàn)的功能,CoffeeFlavor實(shí)現(xiàn)了CoffeeOrder接口,并且必須包含其接口的方法。

// Flyweight object
var CoffeeOrder = {

  // Interfaces
  serveCoffee:function(context){},
    getFlavor:function(){}

};

// ConcreteFlyweight object that creates ConcreteFlyweight
// Implements CoffeeOrder
function CoffeeFlavor( newFlavor ){

    var flavor = newFlavor;

    // If an interface has been defined for a feature
    // implement the feature
    if( typeof this.getFlavor === "function" ){
      this.getFlavor = function() {
          return flavor;
      };
    }

    if( typeof this.serveCoffee === "function" ){
      this.serveCoffee = function( context ) {
        console.log("Serving Coffee flavor "
          + flavor
          + " to table number "
          + context.getTable());
    };     
    }

}

// Implement interface for CoffeeOrder
CoffeeFlavor.implementsFor( CoffeeOrder );

// Handle table numbers for a coffee order
function CoffeeOrderContext( tableNumber ) {
   return{
      getTable: function() {
         return tableNumber;
     }
   };
}

function CoffeeFlavorFactory() {
    var flavors = {},
    length = 0;

    return {
        getCoffeeFlavor: function (flavorName) {

            var flavor = flavors[flavorName];
            if (flavor === undefined) {
                flavor = new CoffeeFlavor(flavorName);
                flavors[flavorName] = flavor;
                length++;
            }
            return flavor;
        },

        getTotalCoffeeFlavorsMade: function () {
            return length;
        }
    };
}

// Sample usage:
// testFlyweight()

function testFlyweight(){

  // The flavors ordered.
  var flavors = new CoffeeFlavor(),

  // The tables for the orders.
    tables = new CoffeeOrderContext(),

  // Number of orders made
    ordersMade = 0,

  // The CoffeeFlavorFactory instance
    flavorFactory;

  function takeOrders( flavorIn, table) {
     flavors[ordersMade] = flavorFactory.getCoffeeFlavor( flavorIn );
     tables[ordersMade++] = new CoffeeOrderContext( table );
  }

   flavorFactory = new CoffeeFlavorFactory();

   takeOrders("Cappuccino", 2);
   takeOrders("Cappuccino", 2);
   takeOrders("Frappe", 1);
   takeOrders("Frappe", 1);
   takeOrders("Xpresso", 1);
   takeOrders("Frappe", 897);
   takeOrders("Cappuccino", 97);
   takeOrders("Cappuccino", 97);
   takeOrders("Frappe", 3);
   takeOrders("Xpresso", 3);
   takeOrders("Cappuccino", 3);
   takeOrders("Xpresso", 96);
   takeOrders("Frappe", 552);
   takeOrders("Cappuccino", 121);
   takeOrders("Xpresso", 121);

   for (var i = 0; i < ordersMade; ++i) {
       flavors[i].serveCoffee(tables[i]);
   }
   console.log(" ");
   console.log("total CoffeeFlavor objects made: " +  flavorFactory.getTotalCoffeeFlavorsMade());
} <span style="line-height:1.5;font-family:'sans serif', tahoma, verdana, helvetica;font-size:10pt;"></span>

轉(zhuǎn)換代碼為使用享元模式

接下來(lái),讓我們通過(guò)實(shí)現(xiàn)一個(gè)管理一個(gè)圖書(shū)館中所有書(shū)籍的系統(tǒng)來(lái)繼續(xù)觀察享元。分析得知每一本書(shū)的重要元數(shù)據(jù)如下:

  • ID
  • 標(biāo)題
  • 作者
  • 類型
  • 總頁(yè)數(shù)
  • 出版商ID
  • ISBN

我們也將需要下面一些屬性,來(lái)跟蹤哪一個(gè)成員是被借出的一本特定的書(shū),借出它們的日期,還有預(yù)計(jì)的歸還日期。

  • 借出日期
  • 借出的成員
  • 規(guī)定歸還時(shí)間
  • 可用性
var Book = function( id, title, author, genre, pageCount,publisherID, ISBN, checkoutDate, checkoutMember, dueReturnDate,availability ){

   this.id = id;
   this.title = title;
   this.author = author;
   this.genre = genre;
   this.pageCount = pageCount;
   this.publisherID = publisherID;
   this.ISBN = ISBN;
   this.checkoutDate = checkoutDate;
   this.checkoutMember = checkoutMember;
   this.dueReturnDate = dueReturnDate;
   this.availability = availability;

};

Book.prototype = {

  getTitle: function () {
     return this.title;
  },

  getAuthor: function () {
     return this.author;
  },

  getISBN: function (){
     return this.ISBN;
  },

  // For brevity, other getters are not shown
  updateCheckoutStatus: function( bookID, newStatus, checkoutDate , checkoutMember, newReturnDate ){

     this.id  = bookID;
     this.availability = newStatus;
     this.checkoutDate = checkoutDate;
     this.checkoutMember = checkoutMember;
     this.dueReturnDate = newReturnDate;

  },

  extendCheckoutPeriod: function( bookID, newReturnDate ){

      this.id =  bookID;
      this.dueReturnDate = newReturnDate;

  },

  isPastDue: function(bookID){

     var currentDate = new Date();
     return currentDate.getTime() > Date.parse( this.dueReturnDate );

   }
};

這對(duì)于最初小規(guī)模的藏書(shū)可能工作得還好,然而當(dāng)圖書(shū)館擴(kuò)充至每一本書(shū)的多個(gè)版本和可用的備份,這樣一個(gè)大型的庫(kù)存,我們會(huì)發(fā)現(xiàn)管理系統(tǒng)的運(yùn)行隨著時(shí)間的推移會(huì)越來(lái)越慢。使用成千上萬(wàn)的書(shū)籍對(duì)象可能會(huì)壓倒內(nèi)存,而我們可以通過(guò)享元模式的提升來(lái)優(yōu)化我們的系統(tǒng)。

現(xiàn)在我們可以像下面這樣將我們的數(shù)據(jù)分離成為內(nèi)在和外在的狀態(tài):同書(shū)籍對(duì)象(標(biāo)題,版權(quán)歸屬)相關(guān)的數(shù)據(jù)是內(nèi)在的,而借出數(shù)據(jù)(借出成員,規(guī)定歸還日期)則被看做是外在的。這實(shí)際上意味著對(duì)于每一種書(shū)籍屬性的組合僅需要一個(gè)書(shū)籍對(duì)象。這仍然具有相當(dāng)大的數(shù)量,但相比之前已經(jīng)得到大大的縮減了。

下面的書(shū)籍元數(shù)據(jù)組合的單一實(shí)體將在所有帶有一個(gè)特定標(biāo)題的書(shū)籍拷貝中共享。

// Flyweight optimized version
var Book = function ( title, author, genre, pageCount, publisherID, ISBN ) {

    this.title = title;
    this.author = author;
    this.genre = genre;
    this.pageCount = pageCount;
    this.publisherID = publisherID;
    this.ISBN = ISBN;

};

如我們所見(jiàn),外在狀態(tài)已經(jīng)被移除了。從圖書(shū)館借出所要做的一切都被轉(zhuǎn)移到一個(gè)管理器中,由于對(duì)象數(shù)據(jù)現(xiàn)在是分段的,工廠可以被用來(lái)做實(shí)例化。

一個(gè)基本工廠

現(xiàn)在讓我們定義一個(gè)非?;镜墓S。我們用它做的工作是,執(zhí)行一個(gè)檢查來(lái)看看一本給定標(biāo)題的書(shū)是不是之前已經(jīng)在系統(tǒng)內(nèi)創(chuàng)建過(guò)了;如果創(chuàng)建過(guò)了,我們就返回它 - 如果沒(méi)有,一本新書(shū)就會(huì)被創(chuàng)建并保存,使得以后可以訪問(wèn)它。這確保了為每一條本質(zhì)上唯一的數(shù)據(jù),我們只創(chuàng)建了一份單一的拷貝:

// Book Factory singleton
var BookFactory = (function () {
  var existingBooks = {}, existingBook;

  return {
    createBook: function ( title, author, genre, pageCount, publisherID, ISBN ) {

      // Find out if a particular book meta-data combination has been created before
      // !! or (bang bang) forces a boolean to be returned
      existingBook = existingBooks[ISBN];
      if ( !!existingBook ) {
        return existingBook;
      } else {

        // if not, let's create a new instance of the book and store it
        var book = new Book( title, author, genre, pageCount, publisherID, ISBN );
        existingBooks[ISBN] = book;
        return book;

      }
    }
  };

});

管理外在狀態(tài)

下一步,我們需要將那些從Book對(duì)象中移除的狀態(tài)存儲(chǔ)到某一個(gè)地方——幸運(yùn)的是一個(gè)管理器(我們會(huì)將其定義成一個(gè)單例)可以被用來(lái)封裝它們。書(shū)籍對(duì)象和借出這些書(shū)籍的圖書(shū)館成員的組合將被稱作書(shū)籍借出記錄。這些我們的管理器都將會(huì)存儲(chǔ),并且也包含我們?cè)趯?duì)Book類進(jìn)行享元優(yōu)化期間剝離的同借出相關(guān)的邏輯。

// BookRecordManager singleton
var BookRecordManager = (function () {

  var bookRecordDatabase = {};

  return {
    // add a new book into the library system
    addBookRecord: function ( id, title, author, genre, pageCount, publisherID, ISBN, checkoutDate, checkoutMember, dueReturnDate, availability ) {

      var book = bookFactory.createBook( title, author, genre, pageCount, publisherID, ISBN );

      bookRecordDatabase[id] = {
        checkoutMember: checkoutMember,
        checkoutDate: checkoutDate,
        dueReturnDate: dueReturnDate,
        availability: availability,
        book: book
      };
    },
    updateCheckoutStatus: function ( bookID, newStatus, checkoutDate, checkoutMember, newReturnDate ) {

      var record = bookRecordDatabase[bookID];
      record.availability = newStatus;
      record.checkoutDate = checkoutDate;
      record.checkoutMember = checkoutMember;
      record.dueReturnDate = newReturnDate;
    },

    extendCheckoutPeriod: function ( bookID, newReturnDate ) {
      bookRecordDatabase[bookID].dueReturnDate = newReturnDate;
    },

    isPastDue: function ( bookID ) {
      var currentDate = new Date();
      return currentDate.getTime() > Date.parse( bookRecordDatabase[bookID].dueReturnDate );
    }
  };

});

這些改變的結(jié)果是所有從Book類中擷取的數(shù)據(jù)現(xiàn)在被存儲(chǔ)到了BookManager單例(BookDatabase)的一個(gè)屬性之中——與我們以前使用大量對(duì)象相比可以被認(rèn)為是更加高效的東西。同書(shū)籍借出相關(guān)的方法也被設(shè)置在這里,因?yàn)樗鼈兲幚淼臄?shù)據(jù)是外在的而不內(nèi)在的。

這個(gè)過(guò)程確實(shí)給我們最終的解決方法增加了一點(diǎn)點(diǎn)復(fù)雜性,然而同已經(jīng)明智解決的數(shù)據(jù)性能問(wèn)題相比,這只是一個(gè)小擔(dān)憂,如果我們有同一本書(shū)的30份拷貝,現(xiàn)在我們只需要存儲(chǔ)它一次就夠了。每一個(gè)函數(shù)也會(huì)占用內(nèi)存。使用享元模式這些函數(shù)只在一個(gè)地方存在(就是在管理器上),并且不是在每一個(gè)對(duì)象上面,這節(jié)約了內(nèi)存上的使用。

享元模式和DOM

DOM(文檔對(duì)象模型)支持兩種允許對(duì)象偵聽(tīng)事件的方法——自頂向下(事件捕獲)或者自底向下(時(shí)間冒泡)。 在事件捕獲中,事件一開(kāi)始會(huì)被最外面的元素捕獲,并且傳播到最里面的元素。在事件冒泡中,事件被捕獲并且被賦給了最里面的元素,然后傳播到最外面的元素。

在此背景下描述享元模式的最好隱喻來(lái)自Gary Chisholm寫(xiě)的文章,這里摘錄了一點(diǎn)點(diǎn):

嘗試用一種池塘的方式思考享元模式。一只魚(yú)張開(kāi)了它的嘴巴(事件發(fā)生了),泡泡一直要上升到表面(冒泡),當(dāng)泡泡到達(dá)表面時(shí),停泊在頂部的一直蒼蠅飛走了(動(dòng)作執(zhí)行)。在這個(gè)示例中我們能夠很容易的將魚(yú)張開(kāi)嘴巴轉(zhuǎn)換為按鈕被點(diǎn)擊了一下,將泡泡轉(zhuǎn)換為冒泡效果,而蒼蠅飛走了表示一些需要運(yùn)行的函數(shù)。

冒泡被引入用來(lái)處理單個(gè)事件(比如:一次點(diǎn)擊)可能會(huì)由在DOM層級(jí)中的不同級(jí)別的多個(gè)事件處理器處理,這樣的場(chǎng)景。這在哪里發(fā)生了,事件冒泡就會(huì)為在盡可能最低的級(jí)別定義的事件處理器執(zhí)行。從那里開(kāi)始,事件向上冒泡,一直到包含比應(yīng)該包含的更高層級(jí)的元素。

享元模式可用來(lái)進(jìn)一步調(diào)整事件冒泡過(guò)程,這我們很快就將會(huì)看到。

例子1:集中式事件處理

一起來(lái)看看我們第一例子,當(dāng)用戶有個(gè)動(dòng)作(如點(diǎn)擊或是鼠標(biāo)移動(dòng))時(shí)我們將有很多相似的文檔對(duì)象以及相似的行為要處理。一般情況下,當(dāng)我們構(gòu)建手風(fēng)琴式控件,菜單以及其它列表控件時(shí),就會(huì)在每一個(gè)超鏈接元素父容器里綁定點(diǎn)擊事件(如,$('ul li a').on(..)(jQuery代碼,譯者注))。我們可以方便的在可以監(jiān)聽(tīng)事件容器里添加Flyweight,而不是在很多元素里綁定點(diǎn)擊事件。這樣就可處理或是簡(jiǎn)單或是復(fù)雜的需求。

提到組件的類型,經(jīng)常會(huì)涉及到很多部分都有同樣重復(fù)的標(biāo)簽(如,手風(fēng)琴式控件),這是個(gè)好機(jī)會(huì),每個(gè)元素都有可能被點(diǎn)擊的行為,而且基本上用相同的類。我們可以用Flyweight來(lái)構(gòu)建一個(gè)基本的手風(fēng)琴控件。

這里我們使用一個(gè)stateManager命名空間來(lái)封裝我們的享元邏輯,同時(shí)使用jQuery來(lái)把初始點(diǎn)擊事件綁定到一個(gè)div容器上。為了確保頁(yè)面上沒(méi)有其他程序邏輯把類似的處理器綁定到該容器上,首先使用了一個(gè)unbind事件。

現(xiàn)在明確的確立一下容器中的那個(gè)子元素會(huì)被點(diǎn)擊,我們使用一次對(duì)target的檢查來(lái)提供對(duì)被點(diǎn)擊元素的引用,而不管它的父元素是誰(shuí)。然后我們利用該信息來(lái)處理點(diǎn)擊事件,而實(shí)際上不需要在頁(yè)面裝載時(shí)把該事件綁定到具體的子元素上。

HTML

<div id="container">
   <div class="toggle" href="#">More Info (Address)
       <span class="info">
           This is more information
       </span></div>
   <div class="toggle" href="#">Even More Info (Map)
       <span class="info">
          <iframe src="http://www.map-generator.net/extmap.php?name=London&amp;address=london%2C%20england&amp;width=500...gt;"</iframe>
       </span>
   </div>
</div>

JavaScript

var stateManager = {

  fly: function () {

    var self = this;

    $( "#container" ).unbind().on( "click" , function ( e ) {
      var target = $( e.originalTarget || e.srcElement );
        if ( target.is( "div.toggle") ) {
          self.handleClick( target );
        }
    });
  },

  handleClick: function ( elem ) {
    elem.find( "span" ).toggle( "slow" );
  }
};

這樣做的好處是,我們把許多不相關(guān)的動(dòng)作轉(zhuǎn)換為一個(gè)可以共享的動(dòng)作(也許會(huì)保存在內(nèi)存中)。

示例2:使用享元進(jìn)行性能優(yōu)化

在我們的第二個(gè)示例中,我們將會(huì)引述通過(guò)使用jQuery的享元可以獲得的一些更多的性能上的收獲。

Jame Padolsey 以前寫(xiě)過(guò)一篇叫做76比特的文章,講述更快的jQuery,在其中他提醒我們每一次jQuery觸發(fā)了一個(gè)回調(diào),不管是什么類型(過(guò)濾器,每一個(gè),事件處理器),我們都能夠通過(guò)this關(guān)鍵字訪問(wèn)函數(shù)的上下文(與它相關(guān)的DOM元素)。

不幸的是,我們中的許多人已經(jīng)習(xí)慣將this封裝到$()或者jQuery()中的想法,這意味著新的jQuery實(shí)體沒(méi)必要每次都被構(gòu)造出來(lái),而是簡(jiǎn)單的這樣做:

$("div").on( "click", function () {
  console.log( "You clicked: " + $( this ).attr( "id" ));
});

// we should avoid using the DOM element to create a
// jQuery object (with the overhead that comes with it)
// and just use the DOM element itself like this:

$( "div" ).on( "click", function () {
  console.log( "You clicked:"  + this.id );
});

James想要下面的場(chǎng)景中使用jQuery的jQuery.text,然而他不能茍同一個(gè)新的jQuery對(duì)象必須在每次迭代中創(chuàng)建的概念。

$( "a" ).map( function () {
  return $( this ).text();
});

現(xiàn)在就使用jQuery的工具方法進(jìn)行多余的包裝而言,使用jQuery.methodName(如,jQuery.text)比jQuery.fn.methodName(如,jQuery.fn.text)更好,這里methodName代表了一種使用的工具,如each()或者text。這避免了調(diào)用更深遠(yuǎn)級(jí)別的抽象,或者每一次當(dāng)我們的函數(shù)被調(diào)用時(shí)就構(gòu)造一個(gè)新的jQuery對(duì)象,因?yàn)槎x了jQuery.methodName的庫(kù)本身在更底層使用jQuery.fn.methodName驅(qū)動(dòng)的。

然而由于并不是所有jQuery的方法都有相應(yīng)的單節(jié)點(diǎn)功能,Padolsey根據(jù)這個(gè)創(chuàng)意設(shè)計(jì)了jQuery.single工具。 這里的創(chuàng)意是一個(gè)單獨(dú)的jQuery對(duì)象會(huì)被被創(chuàng)建出來(lái)并且用于每一次對(duì)jQuery.single的調(diào)用(有意義的是僅有一個(gè)jQuery對(duì)象會(huì)被創(chuàng)建出來(lái))。對(duì)于此的實(shí)現(xiàn)可以在下面看到,而且由于我們將來(lái)自多個(gè)可能的對(duì)象的數(shù)據(jù)整合到一個(gè)更加集中的單一結(jié)構(gòu)中,技術(shù)上講,它也是一個(gè)享元。

jQuery.single = (function( o ){

   var collection = jQuery([1]);
   return function( element ) {

       // Give collection the element:
       collection[0] = element;

        // Return the collection:
       return collection;

   };
});

對(duì)于這個(gè)的帶有調(diào)用鏈的動(dòng)作的示例如下:

$( "div" ).on( "click", function () {

   var html = jQuery.single( this ).next().html();
   console.log( html );

});

注意:盡管我們可能相信通過(guò)簡(jiǎn)單的緩存我們的jQuery代碼會(huì)提供出同等良好的性能收獲,但Padolsey聲稱$.single()仍然值得使用,且表現(xiàn)更好。那并不是說(shuō)不使用任何的緩存,只要對(duì)這種方法的助益做到心里有數(shù)就行。想要對(duì)$.single有更加詳細(xì)的了解,建議你卻讀一讀Padolsey完整的文章。

上一篇:MVC下一篇:MVP