学习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 相关文章推荐
推荐自用 Javascript 缩图函数 (onDOMLoaded)……
Oct 23 Javascript
JavaScript 数组运用实现代码
Apr 13 Javascript
jquery插件制作 提示框插件实现代码
Aug 17 Javascript
用Js实现的动态增加表格示例自己写的
Oct 21 Javascript
node.js中的path.basename方法使用说明
Dec 09 Javascript
jQuery实现tag便签去重效果的方法
Jan 20 Javascript
Javascript实现图片懒加载插件的方法
Oct 20 Javascript
JavaScript限定范围拖拽及自定义滚动条应用(3)
May 17 Javascript
JavaScript实现移动端轮播效果
Jun 06 Javascript
AngularJS 实现购物车全选反选功能
Oct 24 Javascript
JavaScript 变量,数据类型基础实例详解【变量、字符串、数组、对象等】
Jan 04 Javascript
基于js实现数组相邻元素上移下移
May 19 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
Discuz 6.0+ 批量注册用户名
2009/09/13 PHP
30 个很棒的PHP开源CMS内容管理系统小结
2011/10/14 PHP
Yii调试查看执行SQL语句的方法
2016/07/15 PHP
总结PHP如何获取当前主机、域名、网址、路径、端口和参数等
2016/09/09 PHP
实现PHP搜索加分页
2016/10/12 PHP
PHP使用PDO 连接与连接管理操作实例分析
2020/04/21 PHP
理解Javascript_09_Function与Object
2010/10/16 Javascript
更换select下拉菜单背景样式的实现代码
2011/12/20 Javascript
jquery插件如何使用 jQuery操作Cookie插件使用介绍
2012/12/15 Javascript
jquery监听div内容的变化具体实现思路
2013/11/04 Javascript
浅析js设置控件的readonly与enabled属性问题
2013/12/25 Javascript
js函数定时器实现定时读取系统实时连接数
2014/04/30 Javascript
jQuery实现鼠标滚轮动态改变样式或效果
2015/01/05 Javascript
使用AngularJS中的SCE来防止XSS攻击的方法
2015/06/18 Javascript
举例详解JavaScript中Promise的使用
2015/06/24 Javascript
利用JQuery直接调用asp.net后台的简单方法
2016/10/27 Javascript
javascript 组合按键事件监听实现代码
2017/02/21 Javascript
webpack2.0搭建前端项目的教程详解
2017/04/05 Javascript
angular或者js怎么确定选中ul中的哪几个li
2017/08/16 Javascript
jQuery选择器之属性筛选选择器用法详解
2017/09/19 jQuery
node微信开发之获取access_token+自定义菜单
2019/03/17 Javascript
微信端调取相册和摄像头功能,实现图片上传,并上传到服务器
2019/05/16 Javascript
微信小程序云开发实现云数据库读写权限
2019/05/17 Javascript
python通过ElementTree操作XML获取结点读取属性美化XML
2013/12/02 Python
python中字符串比较使用is、==和cmp()总结
2018/03/18 Python
python基础 range的用法解析
2019/08/23 Python
解决pytorch报错:AssertionError: Invalid device id的问题
2020/01/10 Python
django xadmin 管理器常用显示设置方式
2020/03/11 Python
五分钟学会怎么用Pygame做一个简单的贪吃蛇
2021/01/06 Python
《小儿垂钓》教学反思
2014/02/23 职场文书
个人简历自我评价怎么写
2015/03/10 职场文书
2015仓库保管员年终工作总结
2015/05/13 职场文书
中国文明网2015年“向国旗敬礼”活动网上签名寄语
2015/09/24 职场文书
趣味运动会标语口号
2015/12/26 职场文书
15个值得收藏的JavaScript函数
2021/09/15 Javascript
js不常见操作运算符总结
2021/11/20 Javascript