学习JavaScript设计模式之观察者模式


Posted in Javascript onApril 22, 2020

一、定义

观察者模式(发布-订阅模式):其定义对象间一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都将得到通知。
在JavaScript中,一般使用事件模型来替代传统的观察者模式。
好处:

  • (1)可广泛应用于异步编程中,是一种替代传递回调函数的方案。
  • (2)可取代对象之间硬编码的通知机制,一个对象不用再显示地调用另外一个对象的某个接口。两对象轻松解耦。

二、DOM事件?观察者模式典例

需要监控用户点击document.body的动作,但是我们没有办法预知用户将在什么时间点击。
所以,我们订阅document.body上的click事件,当body节点被点击时,body节点便向订阅者发布这个消息!

document.body.addEventListener("click", function() {
 console.log(1);
}, false);

// 可以多个订阅者
document.body.addEventListener("click", function() {
 console.log(2);
}, false);

doucment.body.click();

某网站有header头部、nav导航、消息列表等模块。这几个模块的渲染都需要获取用户登陆信息。

(1)一般写法:

$.ajax({
 url: './login',
 type: 'post',
 contentType: 'application/json',
 dataType:'json',
 success: function(data) {
 if(data.status === "success") {
  // 登录成功,渲染header、nav
  header.setInfo(data.headerInfo);
  nav.setInfo(data.navInfo);
 }
 }
});

(2)使用观察者模式,很轻松解耦!

$.ajax({
 ...,
 success: function(data) {
 if(data.status === "success") {
  // 登录成功,发布登陆成功消息
  login.trigger("loginsuccess", data);
 }
 }
});

var header = (function() {
 // 监听消息
 login.listen("loginsuccess", function(data){
 header.setInfo(data.headerInfo);
 });
 return {
 setInfo: function(data) {
  console.log("设置header信息");
 }
 };
})();

var nav = (function() {
 login.listen("loginsuccess", function(data){
 nav.setInfo(data.navInfo);
 });
 return {
 setInfo: function(data) {
  console.log("设置nav信息");
 }
 }
})();

三、通用观察者模式

/*
 * 示例:
 * Event.create("namespace1").listen('click', function(a){
 * console.log(a);
 * });
 * Event.create("namespace1").trigger("click", 1);
 */
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);
  }
  return ret;
  };
 // 订阅
 _listen = function(key, fn, cache) {
  if(!cache[key]) {
  cache[key] = [];
  }
  cache[key].push(fn);
 };
 // 移除订阅
 _remove = function(key, cache, fn) {
  if(cache[key]) {
  if(fn) {
   for(var i = cache[key].length; i >=0; i++) {
   if(cache[key][i] === fn) {
    cache[key].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);
  });
 };
 // 创建命名空间
 _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);
   this.listen(key, fn, last);
   },

   remove: function(key, fn, last) {
   _remove(key, cache, fn);
   },

   trigger: function() {
   var fn,
    args,
    _self = this;
   _unshift.call(arguments, cache);
   args = arguments;
   fn = function() {
    return _trigger.apply(_self, args);
   };
   if(offlineStack) {
    return offlineStack.push(fn);
   }
   return fn;
   }
  };

  return namespace ? (namespaceCache[namespace] ? namespaceCache[namespace] : namespaceCache[namespace] = ret) : ret;
  };

 return {
  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.trigger.apply(this, arguments);
  }
 };
 }();
 return Event;
})();

希望本文所述对大家学习javascript程序设计有所帮助。

Javascript 相关文章推荐
IE与FireFox的兼容性问题分析
Apr 22 Javascript
关于Ext中form移除textfield方法:hide(),setVisible(false),remove()
Dec 02 Javascript
asp.net 30分钟掌握无刷新 Repeater
Sep 16 Javascript
jquery中的查找parents与closest方法之间的区别
Dec 02 Javascript
判断复选框是否被选中的两种方法
Jun 04 Javascript
js中一维数组和二位数组中的几个问题示例说明
Jul 17 Javascript
jquery获得同源iframe内body下标签的值的方法
Sep 25 Javascript
使用jQuery将多条数据插入模态框的实现代码
Oct 08 Javascript
js改变style样式和css样式的简单实例
Jun 28 Javascript
javascript DOM的详解及实例代码
Mar 06 Javascript
Vue中封装input组件的实例详解
Oct 17 Javascript
在Vue 中使用Typescript的示例代码
Sep 10 Javascript
JS获取CSS样式(style/getComputedStyle/currentStyle)
Jan 19 #Javascript
详解javascript实现自定义事件
Jan 19 #Javascript
JS拖拽组件学习使用
Jan 19 #Javascript
理解JS绑定事件
Jan 19 #Javascript
AngularJS模块学习之Anchor Scroll
Jan 19 #Javascript
jQuery unbind()方法实例详解
Jan 19 #Javascript
jQuery绑定事件监听bind和移除事件监听unbind用法实例详解
Jan 19 #Javascript
You might like
自己动手,丰衣足食 - 短波框形天线制作
2021/03/01 无线电
php中DOMDocument简单用法示例代码(XML创建、添加、删除、修改)
2010/12/19 PHP
php如何调用webservice应用介绍
2012/11/24 PHP
php通过数组实现多条件查询实现方法(字符串分割)
2014/05/06 PHP
PHP将回调函数作用到给定数组单元的方法
2014/08/19 PHP
PHP冒泡算法详解(递归实现)
2014/11/10 PHP
Thinkphp事务操作实例(推荐)
2017/04/01 PHP
原生js实现给指定元素的后面追加内容
2013/04/10 Javascript
使用Jquery获取带特殊符号的ID 标签的方法
2014/04/30 Javascript
javascript检测是否联网的实现代码
2014/09/28 Javascript
一款基于jQuery的图片场景标注提示弹窗特效
2015/01/05 Javascript
jquery插件pagination实现无刷新ajax分页
2015/09/30 Javascript
基于Node的React图片上传组件实现实例代码
2017/05/10 Javascript
JS+canvas实现的五子棋游戏【人机大战版】
2017/07/19 Javascript
Node.js中,在cmd界面,进入退出Node.js运行环境的方法
2018/05/12 Javascript
js中自定义react数据验证组件实例详解
2018/10/19 Javascript
js针对图片加载失败的处理方法分析
2019/08/24 Javascript
Element Backtop回到顶部的具体使用
2020/07/27 Javascript
vue element-ul实现展开和收起功能的实例代码
2020/11/25 Vue.js
[46:14]VGJ.T vs Liquid 2018国际邀请赛小组赛BO2 第一场 8.19
2018/08/21 DOTA
python类型强制转换long to int的代码
2013/02/10 Python
python并发编程之多进程、多线程、异步和协程详解
2016/10/28 Python
selenium在执行phantomjs的API并获取执行结果的方法
2018/12/17 Python
pycharm创建一个python包方法图解
2019/04/10 Python
Python这样操作能存储100多万行的xlsx文件
2019/04/16 Python
Pycharm和Idea支持的vim插件的方法
2020/02/21 Python
python数据分析工具之 matplotlib详解
2020/04/09 Python
Python离线安装各种库及pip的方法
2020/11/28 Python
美国巧克力喷泉品牌:Sephra
2019/05/05 全球购物
教育实习生的自我评价分享
2013/11/21 职场文书
会计工作心得体会
2014/01/13 职场文书
装饰公司活动策划方案
2014/08/23 职场文书
高二语文教学反思
2016/02/16 职场文书
redis缓存存储Session原理机制
2021/11/20 Redis
Python的三个重要函数详解
2022/01/18 Python
《火纹风花雪月无双》预告“神秘雇佣兵” 紫发剑客
2022/04/13 其他游戏