码迷,mamicode.com
首页 > 编程语言 > 详细

理解《JavaScript设计模式与开发应用》发布-订阅模式的最终版代码

时间:2016-04-28 17:01:42      阅读:327      评论:0      收藏:0      [点我收藏+]

标签:

  最近拜读了曾探所著的《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将其绑定到第一个参数上面呢?希望看到的大大能帮我解释一下,谢谢! 

 

理解《JavaScript设计模式与开发应用》发布-订阅模式的最终版代码

标签:

原文地址:http://www.cnblogs.com/Jewl/p/5443154.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!