理解JavaScript设计模式与开发应用中发布-订阅模式的最终版代码
最近拜读了曾探所著的《JavaScript设计模式与开发应用》一书,在读到发布-订阅模式一章时,作者不仅给出了基本模式的通用版本的发布-订阅模式的代码,最后还做出了扩展,给该模式增加了离线空间功能和命名空间功能,以达到先发布再订阅的功能和防止名称冲突的效果。但是令人感到遗憾的是最终代码并没有给出足够的注释。这让像我一样的小白就感到非常的困惑,于是我将这份最终代码仔细研究了一下,并给出了自己的一些理解,鉴于能力有限,文中观点可能并不完全正确,望看到的大大们不吝赐教,谢谢!
下面是添加了个人注释的最终版代码:
<!DOCTYPE html>
<html>
<head>
<title></title>
<meta charset = "utf-8" />
</head>
<body>
<script type="text/javascript">
var Event = (function(){ //定义立即调用的对象
var global = this,
Event,
_default = 'default';
Event = function(){
var _listen,//私有变量
_trigger,
_remove,
_slice = Array.prototype.slice,
_shift = Array.prototype.shift,
_unshift = Array.prototype.unshift,
namespaceCache = {},
_create,
find,
each = function( ary, fn ){
var ret;
for ( var i = 0, l = ary.length; i < l; i++ ){
var n = ary[i];
ret = fn.call( n, i, n);
//n(args)
}
return ret;
};
_listen = function( key, fn, cache ){
if ( !cache[ key ] ){
cache[ key ] = [];
}
cache[key].push( fn );
};
_remove = function( key, cache ,fn){ if ( cache[ key ] ){
var fns = cache[key];
if( fn ){
for( var i = fns.length - 1; i >= 0; i-- ){
//原文for( var i = cache[ key ].length; i >= 0; i-- ){
//if( cache[ key ] === fn )我认为不妥。
if( fns[i] === fn ){
fns.splice( i, 1 );
}
}
}else{
cache[ key ] = [];
}
}
};
_trigger = function(){
var cache = _shift.call(arguments),
key = _shift.call(arguments),
args = arguments,
_self = this,
ret,
stack = cache[ key ];
if ( !stack || !stack.length ){
return;
}
return each( stack, function(){
return this.apply( _self, args );//_self = object{} //n(args)
});
};
_create = function( namespace ){
var namespace = namespace || _default;
var cache = {},
offlineStack = [],
// 离线事件
ret = {
listen: function( key, fn, last ){
_listen( key, fn, cache );
if ( offlineStack === null ){
return;
}
if ( last === 'last' ){
offlineStack.length && offlineStack.pop()();
}else{
each( offlineStack, function(){
this();
});
}
offlineStack = null;
},
one: function( key, fn, last ){
_remove( key, cache );
//移除已存在的listen事件
this.listen( key, fn ,last );
},
remove: function( key, fn ){
_remove( key, cache ,fn);
},
trigger: function(){
var fn,
args,
_self = this;
_unshift.call( arguments, cache );
args = arguments;
fn = function(){
return _trigger.apply( _self, args );
//_self的作用是将—trigger方法绑定到ret里面来,从而能使用args };
if ( offlineStack ){
return offlineStack.push( fn );
}
return fn();
}
};
return namespace ?
( namespaceCache[ namespace ] ? namespaceCache[ namespace ] :
namespaceCache[ namespace ] = ret )
: ret;
};
return {
//所有方法均先创建一个离线空间 调用create方法,并传递空参数, 返回ret = object{};
create: _create,
one: function( key,fn, last ){
var event = this.create( );
event.one( key,fn,last );
},
remove: function( key,fn ){
var event = this.create( );
event.remove( key,fn );
},
listen: function( key, fn, last ){
var event = this.create( );
event.listen( key, fn, last );
},
trigger: function(){
var event = this.create( );
//event = ret ;
event.trigger.apply( this, arguments );
//将arguments传递给ret.trigger
}
};
}();
return Event;
})();
Event.trigger( 'click', 5 );
// 将其存入offlineStack等待调用
Event.listen( 'click', function( a ){
console.log( a );
});
Event.create( 'namespace1' ).listen( 'click', function( a ){
console.log( a );
});
// namespace的作用是,没有时,我们返回简单的ret对象。有时,我们返回namespase下的一个键值为namespase1的对象 Event.create( 'namespace1' ).trigger( 'click', 1 );
// 将调用namespase1的trigger方法
Event.one('click',function(a){
console.log("this is the one's "+a);
} ,"last");
Event.trigger('click',666);
Event.listen( 'click', function( a ){
console.log( "this is a simple" +a );
});
Event.listen( 'click', function( a ){
console.log( "this is also a simple " +a );
});
Event.trigger('click',"hahaha");
Event.one('click',function(a){
console.log("this is the one's "+a + " and it's the only " + a);
} ,"last");
Event.trigger('click',"hahahahahahahaha");
</script>
</body>
</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设计模式与开发应用中发布-订阅模式的最终版代码的更多相关文章
- react 中发布订阅模式使用
react 中发布订阅模式使用 场景 怎么能将设计模式应用到我们的 React 项目中?以前一直在思考这个问题. 场景一 模块 A 模块 B 需要用到同一个数据 data,A 和 B 都会修改这份数据 ...
- javascript设计模式学习之八_发布订阅(观察者)模式
一.发布订阅模式定义 jQuery中的callbacks,defered,promise本质上就是发布订阅模式的实现.ES6的promise内部实现未开源,不了解具体机制 发布订阅模式又叫做观察者模式 ...
- JS设计模式(5)发布订阅模式
什么是发布订阅模式(观察者模式)? 定义:定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新. 主要解决:一个对象状态改变给其他对象通知的问题,而且 ...
- js 设计模式:观察者和发布订阅模式
总是把这两个当作同一个模式,但其实是不太一样的,现在重温一下. 观察者模式 观察者直接订阅目标,当目标触发事件时,通知观察者进行更新 简单实现 class Observer { constructor ...
- java设计模式之-观察者模式(发布-订阅模式)
1.观察者模式定义 观察者模式定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象. 这个主题对象在状态上发生变化时,会通知所有观察者对象,让它们能够自动更新自己. 2.观察者模式结构 ...
- Javascript中理解发布--订阅模式
Javascript中理解发布--订阅模式 阅读目录 发布订阅模式介绍 如何实现发布--订阅模式? 发布---订阅模式的代码封装 如何取消订阅事件? 全局--发布订阅对象代码封装 理解模块间通信 回到 ...
- [转] Javascript中理解发布--订阅模式
发布订阅模式介绍 发布---订阅模式又叫观察者模式,它定义了对象间的一种一对多的关系,让多个观察者对象同时监听某一个主题对象,当一个对象发生改变时,所有依赖于它的对象都将得到通知. 现实生活中的发布- ...
- 【转】Javascript中理解发布--订阅模式
Javascript中理解发布--订阅模式 阅读目录 发布订阅模式介绍 发布---订阅模式又叫观察者模式,它定义了对象间的一种一对多的关系,让多个观察者对象同时监听某一个主题对象,当一个对象发生改变时 ...
- javascript设计模式——发布订阅模式
前面的话 发布—订阅模式又叫观察者模式,它定义对象间的一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都将得到通知.在javascript开发中,一般用事件模型来替代传统的发布—订阅模 ...
随机推荐
- 【JavaScript】重温Javascript继承机制
上段时间,团队内部有过好几次给力的分享,这里对西风师傅分享的继承机制稍作整理一下,适当加了些口语化的描述,留作备案. 一.讲个故事吧 澄清在先,Java和Javascript是雷锋和雷峰塔的关系.Ja ...
- android之BitmapFactory.Options的使用
怎样获取图片的大小? 首先我们把这个图片转成Bitmap,然后再利用Bitmap的getWidth()和getHeight()方法就可以取到图片的宽高了. 新问题又来了,在通过BitmapFactor ...
- shell 编程中使用到得if语句内判断参数
http://blog.chinaunix.net/uid/21411227/cid-63616-list-1.html 1.判断文件类型 –b 当file存在并且是块文件时返回真 -c 当fil ...
- NopCommerce使用Autofac实现依赖注入
NopCommerce的依赖注入是用的AutoFac组件,这个组件在nuget可以获取,而IOC反转控制常见的实现手段之一就是DI依赖注入,而依赖注入的方式通常有:接口注入.Setter注入和构造函数 ...
- 项目源码--Android聚合视频类播放器
下载源码 技术要点: 1.高效支持主流的视音频格式 2.本地视频的播放与管理 3.聚合电视在线直播 4.聚合优酷.搜狐.乐视.爱奇艺等多种在线视频 5.优质播放,包含播放.暂停,声音.亮度调整等功能 ...
- Java 7之集合类型 - 二叉排序树、平衡树、红黑树---转
http://blog.csdn.net/mazhimazh/article/details/19961017 为了理解 TreeMap 的底层实现,必须先介绍排序二叉树和平衡二叉树,然后继续介绍红黑 ...
- 512字节纠错1位的ECC校验码生成演示
Flash型号: NandFlash型号:TC58NVG2S3ETA00 pagesize: 2KB oobsize : 64B blocksize : 128K 关于ECC可以参考:http:// ...
- jquery 和 $
而jQuery就是jQuery库提供的一个函数.(好像也不仅仅只是函数, 因为还有 $.ajax(options) 这样的使用,等同 jQuery.ajax(options)) 这个函数的作用是根据 ...
- uiview 的transitionWithView 方法使用
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { [UIView transitionWithView:_imageV ...
- Android中“再按一次返回键退出程序”实现
private long exitTime = 0; @Override public boolean onKeyDown(int keyCode, KeyEvent event) { if(keyC ...