学习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获取单元格自定义属性值的代码(IE/Firefox)
Apr 05 Javascript
jQuery判断元素是否是隐藏的代码
Apr 24 Javascript
JqGrid web打印实现代码
May 31 Javascript
JS实现图片预加载无需等待
Dec 21 Javascript
js取消单选按钮选中示例代码
Nov 14 Javascript
javascript实现点击提交按钮后显示loading的方法
Jul 03 Javascript
JS时间特效最常用的三款
Aug 19 Javascript
AngularJS页面访问时出现页面闪烁问题的解决
Mar 06 Javascript
原生JS封装Ajax插件(同域、jsonp跨域)
May 03 Javascript
Vue.js通用应用框架-Nuxt.js的上手教程
Dec 25 Javascript
js实现踩五彩块游戏
Feb 08 Javascript
解决父组件将子组件作为弹窗调用只执行一次created的问题
Jul 24 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调用数据库的存贮过程
2006/10/09 PHP
二十行语句实现从Excel到mysql的转化
2006/10/09 PHP
让你的WINDOWS同时支持MYSQL4,MYSQL4.1,MYSQL5X
2006/12/06 PHP
《PHP边学边教》(02.Apache+PHP环境配置――下篇)
2006/12/13 PHP
php5数字型字符串加解密代码
2008/04/24 PHP
php递归法读取目录及文件的方法
2015/01/30 PHP
Yii框架实现多数据库配置和操作的方法
2017/05/25 PHP
php中html_entity_decode实现HTML实体转义
2018/06/13 PHP
tp5(thinkPHP5)框架实现多数据库查询的方法
2019/01/10 PHP
php设计模式之正面模式实例分析【星际争霸游戏案例】
2020/03/24 PHP
juqery 学习之五 文档处理 插入
2011/02/11 Javascript
JavaScript快速检测浏览器对CSS3特性的支持情况
2012/09/26 Javascript
window.showModalDialog()返回值的学习心得总结
2014/01/07 Javascript
jquery live()调用不存在的解决方法
2014/02/26 Javascript
js加密解密字符串可自定义密码因子
2014/05/13 Javascript
学习JavaScript设计模式之中介者模式
2016/01/14 Javascript
浅析JavaScript函数的调用模式
2016/08/10 Javascript
谈谈对JavaScript原生拖放的深入理解
2016/09/20 Javascript
AngularJS打开页面隐藏显示表达式用法示例
2016/12/25 Javascript
原生js实现电商侧边导航效果
2017/01/19 Javascript
xmlplus组件设计系列之树(Tree)(9)
2017/05/02 Javascript
angular-ngSanitize模块-$sanitize服务详解
2017/06/13 Javascript
vue实现同一个页面可以有多个router-view的方法
2018/09/20 Javascript
js序列化和反序列化的使用讲解
2019/01/19 Javascript
python冒泡排序算法的实现代码
2013/11/21 Python
Python执行时间的计算方法小结
2017/03/17 Python
Python使用ctypes调用C/C++的方法
2019/01/29 Python
Pytorch中accuracy和loss的计算知识点总结
2019/09/10 Python
Python HTMLTestRunner测试报告view按钮失效解决方案
2020/05/25 Python
html5开发之viewport使用
2013/10/17 HTML / CSS
实习期自我鉴定
2013/10/11 职场文书
超市七夕促销活动方案
2014/08/28 职场文书
工人先进事迹材料
2014/12/26 职场文书
三八妇女节主持词
2015/07/04 职场文书
2019最新版试用期劳动合同模板!
2019/07/04 职场文书
JavaGUI模仿QQ聊天功能完整版
2021/07/04 Java/Android