理解JavaScript設計模式與開發應用中發佈-訂閱模式的最終版代碼

来源:http://www.cnblogs.com/Jewl/archive/2016/04/28/5443154.html
-Advertisement-
Play Games

最近拜讀了曾探所著的《JavaScript設計模式與開發應用》一書,在讀到發佈-訂閱模式一章時,作者不僅給出了基本模式的通用版本的發佈-訂閱模式的代碼,最後還做出了擴展,給該模式增加了離線空間功能和命名空間功能,以達到先發佈再訂閱的功能和防止名稱衝突的效果。但是令人感到遺憾的是最終代碼並沒有給出足夠 ...


  最近拜讀了曾探所著的《JavaScript設計模式與開發應用》一書,在讀到發佈-訂閱模式一章時,作者不僅給出了基本模式的通用版本的發佈-訂閱模式的代碼,最後還做出了擴展,給該模式增加了離線空間功能和命名空間功能,以達到先發佈再訂閱的功能和防止名稱衝突的效果。但是令人感到遺憾的是最終代碼並沒有給出足夠的註釋。這讓像我一樣的小白就感到非常的困惑,於是我將這份最終代碼仔細研究了一下,並給出了自己的一些理解,鑒於能力有限,文中觀點可能並不完全正確,望看到的大大們不吝賜教,謝謝!

  下麵是添加了個人註釋的最終版代碼:

  1 <!DOCTYPE html>
  2 <html>
  3 <head>
  4     <title></title>
  5     <meta charset = "utf-8" />
  6 </head>
  7 <body>
  8 <script type="text/javascript">
  9 var Event = (function(){  //定義立即調用的對象
 10         var global = this,  
 11         Event,
 12         _default = 'default';
 13         Event = function(){
 14             var _listen,//私有變數
 15             _trigger,
 16             _remove,
 17             _slice = Array.prototype.slice,
 18             _shift = Array.prototype.shift,
 19             _unshift = Array.prototype.unshift,
 20             namespaceCache = {},
 21             _create,
 22             find,
 23             each = function( ary, fn ){
 24                 var ret;
 25                 for ( var i = 0, l = ary.length; i < l; i++ ){
 26                     var n = ary[i];
 27                     ret = fn.call( n, i, n);
 28                     //n(args)
 29                 }
 30                 return ret;
 31             };
 32             _listen = function( key, fn, cache ){
 33                 if ( !cache[ key ] ){
 34                     cache[ key ] = [];
 35                 }
 36                 cache[key].push( fn );
 37             };
 38             _remove = function( key, cache ,fn){
 39 
 40                 if ( cache[ key ] ){
 41                     var fns = cache[key];
 42                     if( fn ){
 43                         for( var i = fns.length - 1; i >= 0; i-- ){
 44                             //原文for( var i = cache[ key ].length; i >= 0; i-- ){
 45                             //if( cache[ key ] === fn )我認為不妥。
 46                             if( fns[i] === fn ){
 47                                 fns.splice( i, 1 );
 48                             }
 49                         }
 50                     }else{
 51                         cache[ key ] = [];
 52                     }
 53                 }
 54             };
 55             _trigger = function(){
 56                 var cache = _shift.call(arguments),
 57                 key = _shift.call(arguments),
 58                 args = arguments,
 59                 _self = this,
 60                 ret,
 61                 stack = cache[ key ];
 62                 if ( !stack || !stack.length ){
 63                     return;
 64                 }
 65                 return each( stack, function(){
 66                     return this.apply( _self, args );//_self = object{}  //n(args)
 67                 });
 68             };
 69             _create = function( namespace ){
 70                 var namespace = namespace || _default;
 71                 var cache = {},
 72                 offlineStack = [], 
 73                 // 離線事件
 74                 ret = {
 75                     listen: function( key, fn, last ){
 76                         _listen( key, fn, cache );
 77                         if ( offlineStack === null ){
 78                             return;
 79                         }
 80                         if ( last === 'last' ){
 81                             offlineStack.length && offlineStack.pop()(); 
 82                         }else{
 83                             each( offlineStack, function(){
 84                                 this();
 85                             });
 86                         }
 87                         offlineStack = null;
 88                     },
 89                     one: function( key, fn, last ){
 90                         _remove( key, cache );
 91                         //移除已存在的listen事件
 92                         this.listen( key, fn ,last );
 93                     },
 94                     remove: function( key, fn ){
 95                         _remove( key, cache ,fn);
 96                     },
 97                     trigger: function(){
 98                         var fn,
 99                         args,
100                         _self = this;
101                         _unshift.call( arguments, cache );
102                         args = arguments;
103                         fn = function(){
104                             return _trigger.apply( _self, args );
105                             //_self的作用是將—trigger方法綁定到ret裡面來,從而能使用args
106 
107                         };
108                         if ( offlineStack ){
109                             return offlineStack.push( fn );
110                         }
111                         return fn();
112                     }
113                 };
114                 return namespace ?
115                 ( namespaceCache[ namespace ] ? namespaceCache[ namespace ] :
116                     namespaceCache[ namespace ] = ret )
117                 : ret;
118             };
119             return { 
120             //所有方法均先創建一個離線空間 調用create方法,並傳遞空參數, 返回ret = object{};
121                 create: _create,
122                 one: function( key,fn, last ){
123                     var event = this.create( );
124                     event.one( key,fn,last );
125                 },
126                 remove: function( key,fn ){
127                     var event = this.create( );
128                     event.remove( key,fn );
129                 },
130                 listen: function( key, fn, last ){
131                     var event = this.create( );
132                     event.listen( key, fn, last );
133                 },
134                 trigger: function(){
135                     var event = this.create( ); 
136                     //event = ret ;
137                     event.trigger.apply( this, arguments ); 
138                     //將arguments傳遞給ret.trigger
139                 }
140             };
141         }();
142         return Event;
143     })();
144 Event.trigger( 'click', 5 );  
145     // 將其存入offlineStack等待調用
146 Event.listen( 'click', function( a ){     
147 console.log( a );       
148 });
149 Event.create( 'namespace1' ).listen( 'click', function( a ){    
150  console.log( a ); 
151  });  
152   //  namespace的作用是,沒有時,我們返回簡單的ret對象。有時,我們返回namespase下的一個鍵值為namespase1的對象
153  
154 Event.create( 'namespace1' ).trigger( 'click', 1 );
155   // 將調用namespase1的trigger方法 
156 Event.one('click',function(a){
157     console.log("this is the one's "+a);
158 } ,"last");
159 Event.trigger('click',666); 160 Event.listen( 'click', function( a ){ 161 console.log( "this is a simple" +a ); 162 }); 163 Event.listen( 'click', function( a ){ 164 console.log( "this is also a simple " +a ); 165 });
166 Event.trigger('click',"hahaha"); 167 Event.one('click',function(a){ 168 console.log("this is the one's "+a + " and it's the only " + a); 169 } ,"last"); 170 Event.trigger('click',"hahahahahahahaha"); 171 </script> 172 </body> 173 </html>

  我認為對於代碼的理解可以分為兩個階段,第一個階段:理解代碼的含義,明白代碼是怎麼運行的;第二個階段:深刻理解代碼本質,並能夠獨立寫出代碼。當然作為小白的我還沒有能力達到第二階段,也只能講講自己第一階段的理解了。

  有同學曾和我討論過這段代碼裡面one()的作用,通過最後面添加的實例不難理解,它的作用是清除之前存在的(某個命名空間的)訂閱事件,再添加唯一的一個訂閱事件。然後對於一些細節的理解我通過註釋添加在了代碼中,如有感興趣的同學,歡迎前來和我討論,或者有覺得我的觀點有失偏頗的,希望能不吝賜教。

  最後我認為拋開個模塊之間有點複雜的通信外,這段代碼最讓人難以理解的就是this的應用了,JavaScript裡面的this被認為是一個巨大的坑,但運用得當,必會事半功倍。這裡我先簡要談談對this的理解:

  JavaScript裡面的this大致可以分為4種情況:第一種情況,方法調用模式:函數被保存為對象的一個方法,當這個方法被調用時,this指向該對象;第二種情況,函數調用模式:此模式下,this被綁定到全局對象,這被認為是一個設計錯誤;第三種情況,構造器調用模式:如果一個函數前面帶上new來調用,那麼將創建一個隱藏鏈接到該函數的prototype成員的新對象,同時this綁定到新對象上;第四種情況,call,apply調用模式:該模式類似於繼承,將執行call,apply操作的對象綁定到第一個參數上,同時將this綁定到第一個參數上,例如:

<!DOCTYPE html>
<html>
<head>
    <title></title>
    <meta charset="utf-8"> 
    </head>
<body>
<script type="text/javascript">
var name = "I am window";
var obj = {
    name:"sharpxiajun",
    job:"Software",
    ftn01:function(obj){
        obj.show();
    },
    ftn02:function(ftn){
        ftn();
    },
    ftn03:function(ftn){
        ftn.call(this);
    }
};
function Person(name){
    this.name = name;
    this.show = function(){
        console.log("姓名:" + this.name);
        console.log(this);
    }
}
var p = new Person("Person");
obj.ftn01(p);
obj.ftn02(function(){
   console.log(this.name);
   console.log(this);
});
obj.ftn03(function(){
    console.log(this.name);
    console.log(this);
});
</script>
</body>
</html>

實例來源: 夏天的森林 《JavaScript技術難點(三)之this、new、apply和call詳解》

輸出結果為:

   但是發佈-訂閱模式的的this應用依然讓我感到費解:

這兩處this的使用不像平常見到的那種隱式調用或者用做參數,而是直接當做函數使用(表述不定對),這讓我有點難以理解,但是他們達到的效果就是類似的,起到的是傳遞參數的作用。那麼這裡的this我否可以理解為也是通過call,apply將其綁定到第一個參數上面呢?希望看到的大大能幫我解釋一下,謝謝! 

 


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

-Advertisement-
Play Games
更多相關文章
  • 學習要點: 1.實體 2.元數據 3.全局屬性 主講教師:李炎恢 本章主要探討 HTML5 中的 HTML 實體、以及 HTML 核心構成的元數據,最後瞭解一下 HTML 中的全局屬性。 一.實體 HTML 實體就是將有特殊意義的字元通過實體代碼顯示出來。 二.元數據 <meta>元素可以定義文檔中 ...
  • 入行也半年了,無數次的想過寫博客也無數次的想過第一篇會寫什麼,一直沒有落實。今天心血來潮把博客開了,那就寫點東西吧。第一篇就寫一寫看似簡單但又經常不註意到的meta標簽吧。(博主經驗尚淺,有許多理解不到位的地方或者說錯的地方歡迎批評指正) <meta charset="utf-8"/>不說了~這個是 ...
  • 效果:http://hovertree.com/texiao/jquery/61/ jQuery仿阿裡雲購買伺服器選擇時間長度,操作簡單,只需點擊所要選的時間段 代碼: 轉自:http://hovertree.com/h/bjaf/njmbk43d.htm 更多特效:http://www.cnblo ...
  • getElementById():獲取有指定惟一ID屬性值文檔中的元素 getElementsByName(name):返回的是數組 getElementsByTagName():返回具有指定標簽名的元素子元素集合 getAttribute():返回指定屬性名的屬性值 setAttribute(): ...
  • FKP-REST是一套前後端分離,基於javascript的全棧實現,基於node的高性能,易部署性及javascript前後端語言的一致性,學習成本,時間成本及項目快速啟動等等方面,FKP都是一種不錯的解決方案 FED: 前端有完整的腳手架系統,支持代碼的編譯、壓縮、模塊化,及基於Reactjs的 ...
  • 縮略圖組件 縮略圖在網站中最常用的就是產品列表頁面,一行顯示幾張圖片,有的在圖片底下帶有標題、描述內容、按鈕等信息。bootstrap框架將這部分獨立成一個模塊組件,通過類名.thumbnail配合bootstrap的網格系統來實現。下麵是bootstrap縮略圖組件不同版本的源碼文件: LESS ... ...
  • ...
  • 1,Bootstrap 模態對話框和簡單使用 可以使用按鈕或鏈接直接調用模態對話框,這是簡單的用法: 另外,當你需要讓對話框能夠在每次打開時表單數據清空,如下: ...
一周排行
    -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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...