理解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中的回调函数
在Javascript中,函数是第一类对象,这意味着函数可以像对象一样按照第一类管理被使用.既然函数实际上是对象:它们能被“存储”在变量中,能作为函数参数被传递,能在函数中被创建,能从函数中返回. 因 ...
- 用Systemtap探索MySQL
http://www.actionsky.com/docs/archives/168#Systemtap 目录 1 Systemtap 2 Systemtap 观测点的支持程度 2.1 官方编译的My ...
- 在ascx中调用另一个ascx的写法
using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.We ...
- 重置kafka的offset
如果你在使用Kafka来分发消息,在数据处理的过程中可能会出现处理程序出异常或者是其它的错误,会造成数据丢失或不一致.这个时候你也许会想要通过kafka把数据从新处理一遍,我们知道kafka默认会在磁 ...
- 【PHP代码审计】 那些年我们一起挖掘SQL注入 - 8.全局防护盲点的总结下篇
0x01 背景 现在的WEB应用对SQL注入的防护基本都是判断GPC是否开启,然后使用addlashes函数对单引号等特殊字符进行转义.但仅仅使用这样的防护是存在很多盲点的,接上篇http://www ...
- 基于 SquashFS 构建 Linux 可读写文件系统
转载:http://www.oschina.net/question/129540_116839 在当前的嵌入式操作系统开发中,Linux 操作系统通常被压缩成 Image 后存放在 Flash 设备 ...
- 保持查询语法指示的联接顺序Option(Force order)
Option(Force order) 今天和大家分享一下 SQL中强制执行联接顺序Option(Force Order) 一.SQL本身SQL引擎优化已经做的非常好了,但是也有默认的多表连接引擎效果 ...
- Guid vs Int
http://www.uml.org.cn/net/200512283.htm http://www.cnblogs.com/twodays/archive/2004/07/19/25562.aspx ...
- 依赖注入容器Autofac的详解
Autofac和其他容器的不同之处是它和C#语言的结合非常紧密,在使用过程中对你的应用的侵入性几乎为零,更容易与第三方的组件集成,并且开源,Autofac的主要特性如下: 1,灵活的组件实例化:Aut ...
- android基础学习之布局
我学习android第一步接触的就是布局的学习.布局是androidUI的基础,是重中之重.总的来说android有6种布局,分别是线性布局LinearLayout.相对布局RelativeLayou ...