輕鬆掌握:JavaScript享元模式

来源:http://www.cnblogs.com/susufufu/archive/2016/08/27/5812080.html
-Advertisement-
Play Games

享元模式 在JavaScript中,瀏覽器特別是移動端的瀏覽器分配的記憶體很有限,如何節省記憶體就成了一件非常有意義的事情。節省記憶體的一個有效方法是減少對象的數量。 享元模式(Flyweight),運行共用技術有效地支持大量細粒度的對象,避免大量擁有相同內容的小類的開銷(如耗費記憶體),使大家共用一個類( ...


享元模式

在JavaScript中,瀏覽器特別是移動端的瀏覽器分配的記憶體很有限,如何節省記憶體就成了一件非常有意義的事情。節省記憶體的一個有效方法是減少對象的數量。

享元模式(Flyweight),運行共用技術有效地支持大量細粒度的對象,避免大量擁有相同內容的小類的開銷(如耗費記憶體),使大家共用一個類(元類)。

享元模式可以避免大量非常相似類的開銷,在程式設計中,有時需要生產大量細粒度的類實例來表示數據,如果能發現這些實例除了幾個參數以外,開銷基本相同的話,就可以大幅度較少需要實例化的類的數量。如果能把那些參數移動到類實例的外面,在方法調用的時候將他們傳遞進來,就可以通過共用大幅度第減少單個實例 的數目。

在JavaScript中應用享元模式有兩種方式,第一種是應用在數據層上,主要是應用在記憶體里大量相似的對象上;第二種是應用在DOM層上,享元可以用在中央事件管理器上用來避免給父容器里的每個子元素都附加事件句柄

Flyweight中有兩個重要概念--內部狀態intrinsic和外部狀態extrinsic之分,內部狀態就是在對象里通過內部方法管理,而外部信息可以在通過外部刪除或者保存。

說白點,就是先捏一個的原始模型,然後隨著不同場合和環境,再產生各具特征的具體模型,很顯然,在這裡需要產生不同的新對象,所以Flyweight模式中常出現Factory模式,Flyweight的內部狀態是用來共用的,Flyweight factory負責維護一個Flyweight pool(模式池)來存放內部狀態的對象。

我們可以將內部狀態相同的所有對象替換為同一個共用對象,而要創建這樣一個共用對象就需要用到單例工廠方法,而不是普通的構造函數,這樣做可以跟蹤到已經實例化的各個對象,從而僅當所需對象的內部狀態不同於已有對象時才創建一個新對象。對象的外在狀態被保存在一個管理器對象中。在調用對象的方法時,管理器會把這些外在狀態作為參數傳入。

把一個對象的數據保存在兩個不同的對象中(共用對象、管理器對象)

  1. 共用對象(享元對象)
  2. 單例工廠方法(創建共用對象)
  3. 管理器對象(管理外部狀態)

比如圖書館中的一本書可以用一個對象來表示,他有很多屬性

var Book = function( id, title, author, genre, pageCount,publisherID, ISBN, checkoutDate, checkoutMember, dueReturnDate,availability ){
    ...//初始化代碼
}
Book.prototype = {
   getTitle:function(){
       return this.title;
   },
   ...
    // 更新借出狀態方法
    updateCheckoutStatus:function(bookID, newStatus, checkoutDate,checkoutMember, newReturnDate){...},
    //續借
    extendCheckoutPeriod: function(bookID, newReturnDate){...},
    //是否到期
    isPastDue: function(bookID){...}
}

程式剛開始可能沒問題,但是隨著時間的增加,圖書可能大批量增加,並且每種圖書都有不同的版本和數量,你將會發現系統變得越來越慢。幾千個book對象在記憶體里可想而知,我們需要用享元模式來優化。

我們可以將數據分成內部和外部兩種數據,同一本書中,和book對象相關的數據(title,author等)可以歸結為內部屬性,而(checkoutMember,dueReturnDate等)可以歸結為外部屬性。這樣,如下代碼就可以在同一本書里共用同一個對象了,因為不管誰借的書,只要書是同一本書,基本信息是一樣的:

//共用對象
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;
};

讓我們來定義一個基本工廠,用來檢查之前是否創建該book的對象,如果有就返回,沒有就重新創建並存儲以便後面可以繼續訪問,這確保我們為每一種書只創建一個對象:

/* Book工廠 單例 */
var BookFactory = (function(){
   var existingBooks = {};
   return{
       createBook: function(title, author, genre,pageCount,publisherID,ISBN){
       /*查找之前是否創建*/
           var existingBook = existingBooks[ISBN];
           if(existingBook){
                return existingBook;
           }else{
               /* 如果沒有,就創建一個,然後保存*/
               var book = new Book(title, author, genre,pageCount,publisherID,ISBN);
               existingBooks[ISBN] =  book;
               return book;
           }
       }
   }
});

外部狀態,相對就簡單了,除了我們封裝好的book,其它都需要在這裡管理:

/*BookRecordManager 借書管理類 單例*/
var BookRecordManager = (function(){
   var bookRecordDatabase = {};
   return{
       /*添加借書記錄*/
       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);
        }
    };
});

通過這種方式,我們做到了將同一種圖書的相同信息保存在一個bookmanager對象里,而且只保存一份;相比之前的代碼,就可以發現節約了很多記憶體。

對象池

對象池是另外一種性能優化方案,和享元模式有一些相似之處,但沒有分離內部狀態和外部狀態這個過程。

通用對象池實現:
var objectPoolFactory = function (createObjFn) {
    var objectPool = []; //對象池
    return {
        create: function () { //取出
            var obj = objectPool.length === 0 ? createObjFn.apply(this,arguments) : objectPool.shift();
            return obj;
        },
        recover: function (obj) { //收回
            objectPool.push(obj);
        }
    }
};

現在利用objectPoolFactory來創建一個裝載一些iframe的對象池:

var iframeFactory = objectPoolFactory(function () {
    var iframe = document.createElement('iframe');
    document.body.appendChild(iframe);
    iframe.onload = function () {
        iframe.onload = null; //防止iframe重覆載入的bug
        iframeFactory.recover(iframe); //iframe載入完成後往對象池填回節點(收回)
    };
    return iframe;
});
//調用
var iframe1 = iframeFactory.create();
iframe1.src = 'http://www.qq.com';

參考文獻: 《JavaScript模式》 《JavaScript設計模式與開發實踐》
http://www.cnblogs.com/TomXu/archive/2012/04/09/2379774.html


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 項目樣式 需求說明:1.實現新增員工 2.使用DataGrideView空間展示員工信息 3.實現刪除員工信息 4.給主窗體添加右鍵菜單,實現上班簽到和下班簽退 5.選擇簽到,必須檢索員工打卡記錄,如果已簽到,提示不能再簽到 6.選擇簽退,必須檢索員工打卡記錄,如果已簽退,提示不能再簽退 7.使用D ...
  • https://github.com/risedragon/spring-mysqlclient/wiki/spring-mysqlclient-user-guide 開源了一個項目,總結了幾年的資料庫操作實踐經驗,還是蠻有興趣的。 ...
  • 關於shopnc 以下是摘抄自百度百科的關於shopnc的介紹: ShopNC商城系統,是天津市網城天創科技有限責任公司開發的一套多店模式的商城系統。 本系統具有商城系統非常完整和專業的功能與流程,系統包括了訂單管理、商品管理、購物車功能、網上支付功能、信息管理、客戶管理、會員體系設置、優惠促銷、廣 ...
  • 安裝composer: 1、在https://getcomposer.org/download/ 中下載 Composer-Setup.exe 2、安裝composer步驟如下: 至此,composer安裝完成。 安裝laravel: 安裝composer完成後,win+R >> cmd,調出命令行 ...
  • 一、抽象類 抽象類,只為繼承而出現,不定義具體的內容,只規定該有哪些東西 一般抽象類中只放置抽象方法,只規定了返回類型和參數 比如: 人 - 有吃飯,睡覺方法 男人 - 繼承人抽象類,必須實現吃飯,睡覺的方法主體 女人 - 繼承人抽象類,必須實現吃飯,睡覺方法的主體 抽象類中可以有普通屬性,通過子類 ...
  • HTML代碼: jquery代碼: 註:只要修改動畫時間就可以控制滾動的速度。 ...
  • 線上實例 使用方法 ...
  • 現象: 近期在微信中開發了一個電商的平臺,一切介面頁面處理完成後,正式佈置到公眾號,在公眾號上自定義菜單進行平臺時(如:.../index.html),發現了一個很有意思的問題:哪個頁面是從公眾號里點擊進入的平臺的,當切換一兩次頁面時,.../index.html這個頁面就切換不進了。 解決過程: ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...