学习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 相关文章推荐
jquery autocomplete自动完成插件的的使用方法
Aug 07 Javascript
jquery子元素过滤选择器使用示例
Jun 24 Javascript
document.getElementById获取控件对象为空的解决方法
Nov 20 Javascript
JS判断移动端访问设备并加载对应CSS样式
Jun 13 Javascript
JS实现让网页背景图片斜向移动的方法
Feb 25 Javascript
详细解密jsonp跨域请求
Apr 15 Javascript
javascript实现漂亮的拖动层,窗口拖拽特效
Apr 24 Javascript
jQuery Uploadify 上传插件出现Http Error 302 错误的解决办法
Dec 12 Javascript
js实现数字递增特效【仿支付宝我的财富】
May 05 Javascript
微信小程序动态显示项目倒计时效果
Jun 13 Javascript
原生JS实现多个小球碰撞反弹效果示例
Jan 31 Javascript
vue.js-div滚动条隐藏但有滚动效果的实现方法
Mar 03 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
JavaScript创建命名空间的5种写法
2014/06/24 PHP
php采用curl访问域名返回405 method not allowed提示的解决方法
2014/06/26 PHP
php中关于长度计算容易混淆的问题分析
2016/05/27 PHP
php实现批量删除挂马文件及批量替换页面内容完整实例
2016/07/08 PHP
PHPWind9.0手动屏蔽验证码解决后台关闭验证码但是依然显示的问题
2016/08/12 PHP
PHP用户管理中常用接口调用实例及解析(含源码)
2017/03/09 PHP
js对数字的格式化使用说明
2011/01/12 Javascript
JQuery将文本转化成JSON对象需要注意的问题
2011/05/09 Javascript
Eval and new funciton not the same thing
2012/12/27 Javascript
jQuery基于$.ajax设置移动端click超时处理方法
2016/05/14 Javascript
JavaScript中输出信息的方法(信息确认框-提示输入框-文档流输出)
2016/06/12 Javascript
AngularJS递归指令实现Tree View效果示例
2016/11/07 Javascript
基于ajax与msmq技术的消息推送功能实现代码
2016/12/26 Javascript
js实现贪吃蛇小游戏(容易理解)
2017/01/22 Javascript
vue-resource 拦截器使用详解
2017/02/21 Javascript
原生Aajax 和jQuery Ajax 写法个人总结
2017/03/24 jQuery
JS实现运动缓冲效果的封装函数示例
2018/02/18 Javascript
Koa 使用小技巧(小结)
2018/10/22 Javascript
基于element-ui组件手动实现单选和上传功能
2018/12/06 Javascript
Vue scrollBehavior 滚动行为实现后退页面显示在上次浏览的位置
2019/05/27 Javascript
javascript二维数组和对象的深拷贝与浅拷贝实例分析
2019/10/26 Javascript
Node中对非阻塞I/O、事件循环的知识点总结
2020/01/05 Javascript
js实现经典贪吃蛇小游戏
2020/03/19 Javascript
node+vue实现文件上传功能
2020/05/28 Javascript
[03:38]2014DOTA2西雅图国际邀请赛 VG战队巡礼
2014/07/07 DOTA
Python写的PHPMyAdmin暴力破解工具代码
2014/08/06 Python
用Python编写脚本使IE实现代理上网的教程
2015/04/23 Python
python实现扫描日志关键字的示例
2018/04/28 Python
详解Django的CSRF认证实现
2018/10/09 Python
Python configparser模块配置文件过程解析
2020/03/03 Python
浅谈Django QuerySet对象(模型.objects)的常用方法
2020/03/28 Python
python 如何利用argparse解析命令行参数
2020/09/11 Python
Jupyter Notebook安装及使用方法解析
2020/11/12 Python
中国跨境海淘网站:考拉海购
2016/08/01 全球购物
2014年个人教学工作总结
2014/12/09 职场文书
唐山大地震的观后感
2015/06/05 职场文书