观察者模式又叫做发布订阅模式,它定义了一种一对多的关系,让多个观察者对象同时监听某一个主题对象,这个主题对象的状态发生改变时就会通知所有观察着对象。 它是由两类对象组成,主题和观察者,主题负责发布事件,同时观察者通过订阅这些事件来观察该主体,发布者和订阅者是完全解耦的,彼此不知道对方的存在,两者仅仅共享一个自定义事件的名称。

在Nodejs中通过EventEmitter实现了原生的对于这一模式的支持。

在JavaScript中事件监听机制就可以理解为一种观察者模式。通过onclick进行事件绑定,然后通过交互行为进行触发或者事件主动触发。

下面给出一个JS自定义的PubSub,仔细阅读下面这段代码有助于你理解观察者模式。

一、定义观察者类Pubsub

/* Pubsub */
function Pubsub(){
//存放事件和对应的处理方法
this.handles = {};
}

二、实现事件订阅on

//传入事件类型type和事件处理handle
on: function (type, handle) {
if(!this.handles[type]){
this.handles[type] = [];
}
this.handles[type].push(handle);
}

三、实现事件发布emit

emit: function () {
//通过传入参数获取事件类型
var type = Array.prototype.shift.call(arguments);
if(!this.handles[type]){
return false;
}
for (var i = 0; i < this.handles[type].length; i++) {
var handle = this.handles[type][i];
//执行事件
handle.apply(this, arguments);
}
}

需要说明的是Array.prototype.shift.call(arguments)这句代码,arguments对象是function的内置对象,可以获取到调用该方法时传入的实参数组。
shift方法取出数组中的第一个参数,就是type类型。

四、实现事件取消订阅off

off: function (type, handle) {
handles = this.handles[type];
if(handles){
if(!handle){
handles.length = 0;//清空数组
}else{
for (var i = 0; i < handles.length; i++) {
var _handle = handles[i];
if(_handle === handle){
handles.splice(i,1);
}
}
}
}
}

完整代码:

/* Pubsub */
function Pubsub(){
//存放事件和对应的处理方法
this.handles = {};
}
Pubsub.prototype={
//传入事件类型type和事件处理handle
on: function (type, handle) {
if(!this.handles[type]){
this.handles[type] = [];
}
this.handles[type].push(handle);
},
emit: function () {
//通过传入参数获取事件类型
var type = Array.prototype.shift.call(arguments);
if(!this.handles[type]){
return false;
}
for (var i = 0; i < this.handles[type].length; i++) {
var handle = this.handles[type][i];
//执行事件
handle.apply(this, arguments);
}
},
off: function (type, handle) {
handles = this.handles[type];
if(handles){
if(!handle){
handles.length = 0;//清空数组
}else{
for (var i = 0; i < handles.length; i++) {
var _handle = handles[i];
if(_handle === handle){
handles.splice(i,1);
}
}
}
}
}
}

五、测试

 
var p1 = new Pubsub();
p1.on('mm', function (name) {
console.log('mm: '+ name);
});
p1.emit('mm','哈哈哈哈');
console.log('===============');
var p2 = new Pubsub();
var fn = function (name) {
console.log('mm2: '+ name);
};
var fn2 = function (name) {
console.log('mm222: '+ name);
};
p2.on('mm2', fn);
p2.on('mm2', fn2);
p2.emit('mm2','哈2哈2哈2哈2');
console.log('-------------');
p2.off('mm2', fn);
p2.emit('mm2','哈2哈2哈2哈2');
console.log('-------------');
p2.off('mm2');
p2.emit('mm2','哈2哈2哈2哈2');
console.log('-------------');

有关JavaScript的技术要点文章请看上海尚学堂:《JavaScript的文档对象模型DOM》;《js 大厦之JavaScript事件》;《细说JavaScript BOM》等,请多多关注。

JavaScript原生实现观察者模式的更多相关文章

  1. jQuery? 回归JavaScript原生API

    如今技术日新月异,各类框架库也是层次不穷.即便当年漫山红遍的JQuery(让开发者write less, do more,So Perfect!!)如今也有被替代的大势.但JS原生API写法依旧:并且 ...

  2. 据说每个大牛、小牛都应该有自己的库——JavaScript原生对象拓展

    在据说每个大牛.小牛都应该有自己的库——框架篇中我扬言要做个小牛,没想到一天没更新,小伙儿伴们就戏谑的问我,油哥是不是要太监了?其实事情是这个样子的,这不是太监的节奏,一是,关于写个自己的库的想法由来 ...

  3. JavaScript原生折叠扩展收缩菜单带缓冲动画

    JavaScript原生折叠扩展收缩菜单带缓冲动画 @落雨 <div id="div_two" style="display: none;"> &l ...

  4. [转] 有趣的JavaScript原生数组函数

    在JavaScript中,可以通过两种方式创建数组,Array构造函数和 [] 便捷方式, 其中后者为首选方法.数组对象继承自Object.prototype,对数组执行typeof操作符返回‘obj ...

  5. JavaScript原生数组函数

    有趣的JavaScript原生数组函数 在JavaScript中,可以通过两种方式创建数组,构造函数和数组直接量, 其中后者为首选方法.数组对象继承自Object.prototype,对数组执行typ ...

  6. JavaScript原生对象拓展

    JavaScript原生对象拓展 在据说每个大牛.小牛都应该有自己的库——框架篇中我扬言要做个小牛,没想到一天没更新,小伙儿伴们就戏谑的问我,油哥是不是要太监了?其实事情是这个样子的,这不是太监的节奏 ...

  7. 使用 javascript 来实现 观察者模式

    以[猫叫.老鼠跑.主人醒]为例子,使用 javascript 来实现 观察者模式 (有在线演示) 2013-06-24 08:35 by 金色海洋(jyk)阳光男孩, 572 阅读, 4 评论, 收藏 ...

  8. JavaScript原生Array常用方法

    JavaScript原生Array常用方法 在入门Vue时, 列表渲染一节中提到数组的变异方法, 其中包括push(), pop(), shift(), unshift(), splice(), so ...

  9. jQuery 事件绑定 和 JavaScript 原生事件绑定

    总结一下:jQuery 事件绑定 和 JavaScript 原生事件绑定 及 区别 jQuery 事件绑定 jQuery 中提供了四种事件监听绑定方式,分别是 bind.live.delegate.o ...

随机推荐

  1. Git那些事儿

    Git是目前世界上最先进的分布式版本控制系统,适合多人协作开发的大型项目.我平常也经常使用git,来管理自己的几个小项目.简单说说git的原理和git的特点!(只有知道了一个工具的运行原理,设计思路, ...

  2. gk888t打印机安装

    https://jingyan.baidu.com/article/948f5924090c7ad80ff5f9c5.html

  3. 将EditPad Lite 加入鼠标右键

    > 开始 > regedit 如图新建shell及command

  4. 探索未知种族之osg类生物---渲染遍历之裁剪二

    前言 上一节我们大致上过了一遍sceneView::cull()函数,通过研究,我们发现上图中的这一部分的代码才是整个cull过程的核心部分.所以今天我们来仔细的研究一下这一部分. sceneView ...

  5. 3D Graph Neural Networks for RGBD Semantic Segmentation

    3D Graph Neural Networks for RGBD Semantic Segmentation 原文章:https://www.yuque.com/lart/papers/wmu47a ...

  6. Java高级应用简笔

    1. Annotation 使用范围: package, class, method, field 常用: @Override, @Deprecated, @SuppressWarnings 自定义注 ...

  7. mysql的初次使用操作

    一.mysql 登录 mysql 参数 mysql -uroot -p123 -P3306 -h127.0.0.1 127.0.0.1本地回环地址 二.mysql退出 mysql >exit; ...

  8. Android自动化之Monkey测试(二)

    本文主要从以下方面进行分享. 一.查看应用包名二.Monkey启动三.Monkey停止四.Monkey命令五.日志分析 一.查看应用包名 大多数时候,我们都是对特定的应用进行monkey测试,因此需要 ...

  9. MySQL数据库和表名大小写敏感开关的打开办法

    背景:window 10系统安装了MySQL57,使用可视化操作工具Navicat Premium 12进行相关操作,一切比较顺利.但在建立数据库和表时,发现大小写命名不敏感.区分大小写输入,保存后默 ...

  10. 分享一个14年写的用户管理类-swift版

    AccountManager类 14年设计,从swift 0.9开始,迭代到现在swift4.0版本,总体几乎没什么改动,简单稳定. 其实现的思路主要还是借助之前net反射的经验,实现了自动保存用户信 ...