学习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 相关文章推荐
js中通过split函数分割字符串成数组小例子
Sep 21 Javascript
IE中图片的onload事件无效问题和解决方法
Jun 06 Javascript
CSS3,HTML5和jQuery搜索框集锦
Dec 02 Javascript
JQuery中上下文选择器实现方法
May 18 Javascript
jquery实现简单的轮换出现效果实例
Jul 23 Javascript
jQuery 判断是否包含在数组中Array[]的方法
Aug 03 Javascript
Jquery和Js获得元素标签名称的方法总结
Oct 08 Javascript
微信小程序 tabs选项卡效果的实现
Jan 05 Javascript
深究AngularJS之ui-router详解
Jun 13 Javascript
Angular2关于@angular/cli默认端口号配置的问题
Jul 15 Javascript
如何安装控制器JavaScript生成插件详解
Oct 21 Javascript
layui实现图片虚拟路径上传,预览和删除的例子
Sep 25 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 ? EasyUI DataGrid 资料存的方式介绍
2012/11/07 PHP
使用CodeIgniter的类库做图片上传
2014/06/12 PHP
PHP5.5和之前的版本empty函数的不同之处
2014/06/13 PHP
PHP编写的图片验证码类文件分享
2016/06/06 PHP
extjs grid取到数据而不显示的解决
2008/12/29 Javascript
window.js 主要包含了页面的一些操作
2009/12/23 Javascript
函数式 JavaScript(一)简介
2014/07/07 Javascript
JavaScript访问字符串中单个字符的两种方法
2015/07/03 Javascript
jquery实现图片上传之前预览的方法
2015/07/11 Javascript
javascript发送短信验证码实现代码
2015/11/12 Javascript
jQuery绑定事件on()与弹窗的简要概述
2016/04/27 Javascript
js判断输入字符串是否为空、空格、null的方法总结
2016/06/14 Javascript
vue.js指令v-for使用及索引获取
2016/11/03 Javascript
微信小程序 PHP生成带参数二维码
2017/02/21 Javascript
JavaScript实现前端实时搜索功能
2020/03/26 Javascript
lhgcalendar时间插件限制只能选择三个月的实现方法
2017/07/03 Javascript
详解Vue.js在页面加载时执行某个方法
2018/11/20 Javascript
vuex页面刷新导致数据丢失的解决方案
2020/12/10 Vue.js
[01:30]我们共输赢 完美世界城市挑战赛开启全新赛季
2019/04/19 DOTA
详解JavaScript编程中的window与window.screen对象
2015/10/26 Python
使用PyInstaller将python转成可执行文件exe笔记
2018/05/26 Python
python实现五子棋小程序
2019/06/18 Python
Python dict和defaultdict使用实例解析
2020/03/12 Python
python list的index()和find()的实现
2020/11/16 Python
售后服务科岗位职责范文
2013/11/13 职场文书
优秀幼教自荐信
2014/02/03 职场文书
六五普法宣传标语
2014/10/06 职场文书
2015年小学中秋节活动总结
2015/03/23 职场文书
2015年客服工作总结范文
2015/04/02 职场文书
2015年财务人员工作总结
2015/04/10 职场文书
2015年公务员个人工作总结
2015/04/24 职场文书
2015年信息宣传工作总结
2015/05/26 职场文书
python3 hdf5文件 遍历代码
2021/05/19 Python
python 爬取哔哩哔哩up主信息和投稿视频
2021/06/07 Python
教你做个可爱的css滑动导航条
2021/06/15 HTML / CSS
解决vue自定义组件@click点击失效问题
2022/04/30 Vue.js