輕鬆掌握: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
  • 示例項目結構 在 Visual Studio 中創建一個 WinForms 應用程式後,項目結構如下所示: MyWinFormsApp/ │ ├───Properties/ │ └───Settings.settings │ ├───bin/ │ ├───Debug/ │ └───Release/ ...
  • [STAThread] 特性用於需要與 COM 組件交互的應用程式,尤其是依賴單線程模型(如 Windows Forms 應用程式)的組件。在 STA 模式下,線程擁有自己的消息迴圈,這對於處理用戶界面和某些 COM 組件是必要的。 [STAThread] static void Main(stri ...
  • 在WinForm中使用全局異常捕獲處理 在WinForm應用程式中,全局異常捕獲是確保程式穩定性的關鍵。通過在Program類的Main方法中設置全局異常處理,可以有效地捕獲並處理未預見的異常,從而避免程式崩潰。 註冊全局異常事件 [STAThread] static void Main() { / ...
  • 前言 給大家推薦一款開源的 Winform 控制項庫,可以幫助我們開發更加美觀、漂亮的 WinForm 界面。 項目介紹 SunnyUI.NET 是一個基於 .NET Framework 4.0+、.NET 6、.NET 7 和 .NET 8 的 WinForm 開源控制項庫,同時也提供了工具類庫、擴展 ...
  • 說明 該文章是屬於OverallAuth2.0系列文章,每周更新一篇該系列文章(從0到1完成系統開發)。 該系統文章,我會儘量說的非常詳細,做到不管新手、老手都能看懂。 說明:OverallAuth2.0 是一個簡單、易懂、功能強大的許可權+可視化流程管理系統。 有興趣的朋友,請關註我吧(*^▽^*) ...
  • 一、下載安裝 1.下載git 必須先下載並安裝git,再TortoiseGit下載安裝 git安裝參考教程:https://blog.csdn.net/mukes/article/details/115693833 2.TortoiseGit下載與安裝 TortoiseGit,Git客戶端,32/6 ...
  • 前言 在項目開發過程中,理解數據結構和演算法如同掌握蓋房子的秘訣。演算法不僅能幫助我們編寫高效、優質的代碼,還能解決項目中遇到的各種難題。 給大家推薦一個支持C#的開源免費、新手友好的數據結構與演算法入門教程:Hello演算法。 項目介紹 《Hello Algo》是一本開源免費、新手友好的數據結構與演算法入門 ...
  • 1.生成單個Proto.bat內容 @rem Copyright 2016, Google Inc. @rem All rights reserved. @rem @rem Redistribution and use in source and binary forms, with or with ...
  • 一:背景 1. 講故事 前段時間有位朋友找到我,說他的窗體程式在客戶這邊出現了卡死,讓我幫忙看下怎麼回事?dump也生成了,既然有dump了那就上 windbg 分析吧。 二:WinDbg 分析 1. 為什麼會卡死 窗體程式的卡死,入口門檻很低,後續往下分析就不一定了,不管怎麼說先用 !clrsta ...
  • 前言 人工智慧時代,人臉識別技術已成為安全驗證、身份識別和用戶交互的關鍵工具。 給大家推薦一款.NET 開源提供了強大的人臉識別 API,工具不僅易於集成,還具備高效處理能力。 本文將介紹一款如何利用這些API,為我們的項目添加智能識別的亮點。 項目介紹 GitHub 上擁有 1.2k 星標的 C# ...