最近拜读了曾探所著的《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设计模式与开发应用中发布-订阅模式的最终版代码的更多相关文章

  1. react 中发布订阅模式使用

    react 中发布订阅模式使用 场景 怎么能将设计模式应用到我们的 React 项目中?以前一直在思考这个问题. 场景一 模块 A 模块 B 需要用到同一个数据 data,A 和 B 都会修改这份数据 ...

  2. javascript设计模式学习之八_发布订阅(观察者)模式

    一.发布订阅模式定义 jQuery中的callbacks,defered,promise本质上就是发布订阅模式的实现.ES6的promise内部实现未开源,不了解具体机制 发布订阅模式又叫做观察者模式 ...

  3. JS设计模式(5)发布订阅模式

    什么是发布订阅模式(观察者模式)? 定义:定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新. 主要解决:一个对象状态改变给其他对象通知的问题,而且 ...

  4. js 设计模式:观察者和发布订阅模式

    总是把这两个当作同一个模式,但其实是不太一样的,现在重温一下. 观察者模式 观察者直接订阅目标,当目标触发事件时,通知观察者进行更新 简单实现 class Observer { constructor ...

  5. java设计模式之-观察者模式(发布-订阅模式)

    1.观察者模式定义  观察者模式定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象. 这个主题对象在状态上发生变化时,会通知所有观察者对象,让它们能够自动更新自己. 2.观察者模式结构 ...

  6. Javascript中理解发布--订阅模式

    Javascript中理解发布--订阅模式 阅读目录 发布订阅模式介绍 如何实现发布--订阅模式? 发布---订阅模式的代码封装 如何取消订阅事件? 全局--发布订阅对象代码封装 理解模块间通信 回到 ...

  7. [转] Javascript中理解发布--订阅模式

    发布订阅模式介绍 发布---订阅模式又叫观察者模式,它定义了对象间的一种一对多的关系,让多个观察者对象同时监听某一个主题对象,当一个对象发生改变时,所有依赖于它的对象都将得到通知. 现实生活中的发布- ...

  8. 【转】Javascript中理解发布--订阅模式

    Javascript中理解发布--订阅模式 阅读目录 发布订阅模式介绍 发布---订阅模式又叫观察者模式,它定义了对象间的一种一对多的关系,让多个观察者对象同时监听某一个主题对象,当一个对象发生改变时 ...

  9. javascript设计模式——发布订阅模式

    前面的话 发布—订阅模式又叫观察者模式,它定义对象间的一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都将得到通知.在javascript开发中,一般用事件模型来替代传统的发布—订阅模 ...

随机推荐

  1. 【JavaScript】关于JS中的constructor与prototype

    最初对js中 object.constructor 的认识: 在学习JS的面向对象过程中,一直对constructor与prototype感到很迷惑,看了一些博客与书籍,觉得自己弄明白了,现在记录如下 ...

  2. 【JavsScript】JavaScript MVC 框架技术选型

    你很喜欢Gmail和Trello之类的单页面应用,但是不太确定该从何开始.也许你的JavaScript代码是如此的杂乱无章,以致于你很想在下一个项目上尝试下JavaScript MVC库和框架,却苦于 ...

  3. C# redis 分布式session存储

    https://github.com/uliian/SessionExtentionStore 一个基于Redis的Session存储扩展方案,解决ASP.NET中Session的局限性和跨应用程序使 ...

  4. jquery easyui from 表单返回乱码!

    如果用easyui的form进行提交,必须在<form>标签中加入属性method="post",即<form method="post"&g ...

  5. cocos2d-x make: *** [clean-box2d_static-armeabi] Error 1

    /cygdrive/d/android-ndk-r8e/build/core/build-binary.mk:52: recipe for target `clean-cocos_curl_stati ...

  6. jquery 验证控件

    最近应公司要求做了一个jquery的示例文件,包括:模态窗口怎么实现:jquery validate下的校验:怎么做图片特效:怎么实现异步操作:实现图片上传剪切效果等很多特效: 这里把jquery校验 ...

  7. WIN7 下 Qt Creator 安装 QWT

    WIN7 下 Qt Creator 安装 QWT 环境:WIN7 +QT Creator2.6.2 1.下载QWT源代码 qwt-6.1-rc3.zip 2 编译QWT  open projects- ...

  8. 向linux内核版本号添加字符/为何有时会自动添加“+”号

    转载:http://blog.csdn.net/adaptiver/article/details/7225980 1.   引子 编译2.6.35.7 kernel版本的时候发现,“2.6.35.7 ...

  9. Python-Day11 RabbitMQ/redis

    写在前面: 好久不写了,实在是不想写,坚持果然是一件不容易的事情. 我喜欢玩,我更爱学习,然而喜欢是放肆,爱是克制,哈哈.每天上班有些忙就不想动,只想在床上翻滚或者鏖战召唤师峡谷.上班闲着时想了想,一 ...

  10. 【阿里云产品公测】大数据下精确快速搜索OpenSearch

    [阿里云产品公测]大数据下精确快速搜索OpenSearch 作者:阿里云用户小柒2012 相信做过一两个项目的人都会遇到上级要求做一个类似百度或者谷歌的站内搜索功能.传统的sql查询只能使用like ...