学习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 相关文章推荐
Google Map Api和GOOGLE Search Api整合实现代码
Jul 18 Javascript
模仿JQuery sortable效果 代码有错但值得看看
Nov 05 Javascript
javascript中怎么做对象的类型判断
Nov 11 Javascript
一个仿糯米弹框效果demo
Jul 22 Javascript
Spring mvc 接收json对象
Dec 10 Javascript
jQuery通过ajax快速批量提交表单数据
Oct 25 Javascript
详解JavaScript按概率随机生成事件
Aug 02 Javascript
微信小程序按钮去除边框线分享页面功能
Aug 27 Javascript
vue v-for循环重复数据无法添加问题解决方法【加track-by='索引'】
Mar 15 Javascript
layui导出所有数据的例子
Sep 10 Javascript
原生JS实现留言板
Mar 26 Javascript
Vue 样式切换及三元判断样式关联操作
Aug 09 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
php数组的概述及分类与声明代码演示
2013/02/26 PHP
CI框架自动加载session出现报错的解决办法
2014/06/17 PHP
PHP生成RSS文件类实例
2014/12/05 PHP
深入浅出php socket编程
2015/05/13 PHP
thinkPHP5.0框架应用请求生命周期分析
2017/03/25 PHP
当jQuery1.7遇上focus方法的问题
2014/01/26 Javascript
Javascript排序算法之计数排序的实例
2014/04/05 Javascript
禁用Tab键JS代码兼容Firefox和IE
2014/04/18 Javascript
innerHTML在IE中报错解决方案
2014/12/15 Javascript
javascript图片滑动效果实现
2021/01/28 Javascript
Bootstrap基本插件学习笔记之Popover提示框(19)
2016/12/08 Javascript
jQuery实现文档树效果
2017/02/20 Javascript
vue快捷键与基础指令详解
2017/06/01 Javascript
关于前后端json数据的发送与接收详解
2017/07/30 Javascript
Angular中使用MathJax遇到的一些问题
2017/12/15 Javascript
微信小程序实现换肤功能
2018/03/14 Javascript
如何在Node和浏览器控制台中打印彩色文字
2020/01/09 Javascript
浅谈鸿蒙 JavaScript GUI 技术栈
2020/09/17 Javascript
详解datagrid使用方法(重要)
2020/11/06 Javascript
Python编写一个优美的下载器
2018/04/15 Python
使用Python OpenCV为CNN增加图像样本的实现
2019/06/10 Python
GDAL 矢量属性数据修改方式(python)
2020/03/10 Python
基于Keras中Conv1D和Conv2D的区别说明
2020/06/19 Python
全球性的在线购物网站:Zapals
2017/03/22 全球购物
Myprotein中国网站:欧洲畅销运动营养品牌
2021/02/11 全球购物
金山毒霸系列的笔试题
2013/04/13 面试题
27个经典Linux面试题及答案,你知道几个?
2014/03/11 面试题
优秀毕业生求职信范文
2014/01/02 职场文书
集体婚礼策划方案
2014/02/22 职场文书
司法工作人员群众路线对照检查材料思想汇报
2014/09/30 职场文书
早会开场白台词大全
2015/06/01 职场文书
行政处罚告知书
2015/07/01 职场文书
早安问候语大全
2015/11/10 职场文书
Mysql基础之常见函数
2021/04/22 MySQL
python flask框架快速入门
2021/05/14 Python
Django框架中表单的用法
2022/06/10 Python