学习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的ajax需要注意的地方dataType的设置
Aug 12 Javascript
JS比较2个日期间隔的示例代码
Apr 15 Javascript
js实现网页右上角滑出会自动消失大幅广告的方法
Feb 27 Javascript
利用js获取下拉框中所选的值
Dec 01 Javascript
bootstrap table实例详解
Jan 06 Javascript
javascript内存分配原理实例分析
Apr 10 Javascript
JavaScript实现瀑布流图片效果
Jun 30 Javascript
解决vue-router在同一个路由下切换,取不到变化的路由参数问题
Sep 01 Javascript
Puppeteer环境搭建的详细步骤
Sep 21 Javascript
微信小程序textarea层级过高(盖住其他元素)问题的解决办法
Mar 04 Javascript
node.js实现http服务器与浏览器之间的内容缓存操作示例
Feb 11 Javascript
vue实现简单加法计算器
Oct 22 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 将逗号、空格、回车分隔的字符串转换为数组的函数
2012/06/07 PHP
PHP 生成N个不重复的随机数
2015/01/21 PHP
php输出全球各个时区列表的方法
2015/03/31 PHP
php通过获取头信息判断图片类型的方法
2015/06/26 PHP
PHP经典面试题之设计模式(经常遇到)
2015/10/15 PHP
Laravel5框架添加自定义辅助函数的方法
2018/08/01 PHP
javascript this用法小结
2008/12/19 Javascript
基于jQuery的js分页代码
2010/06/10 Javascript
Firefox和IE兼容性问题及解决方法总结
2013/10/08 Javascript
JavaScript Ajax Json实现上下级下拉框联动效果实例代码
2013/11/23 Javascript
plupload+artdialog实现多平台上传文件
2016/07/19 Javascript
利用Angularjs实现幻灯片效果
2016/09/07 Javascript
微信小程序 Canvas增强组件实例详解及源码分享
2017/01/04 Javascript
javascript 中的try catch应用总结
2017/04/01 Javascript
微信小程序组件 marquee实例详解
2017/06/23 Javascript
Vue $mount实战之实现消息弹窗组件
2019/04/22 Javascript
JS中使用react-tooltip插件实现鼠标悬浮显示框
2019/05/15 Javascript
vue input标签通用指令校验的实现
2019/11/05 Javascript
js 计数排序的实现示例(升级版)
2020/01/12 Javascript
[01:10:30]DOTA2-DPC中国联赛正赛 Dragon vs Dynasty BO3 第一场 3月4日
2021/03/11 DOTA
在Linux命令行终端中使用python的简单方法(推荐)
2017/01/23 Python
Python学习入门之区块链详解
2017/07/25 Python
Python中用post、get方式提交数据的方法示例
2017/09/22 Python
python 中的list和array的不同之处及转换问题
2018/03/13 Python
Python实现深度遍历和广度遍历的方法
2019/01/22 Python
python实现基于朴素贝叶斯的垃圾分类算法
2019/07/09 Python
python解析xml文件方式(解析、更新、写入)
2020/03/05 Python
简单了解pytest测试框架setup和tearDown
2020/04/14 Python
突袭HTML5之Javascript API扩展3—本地存储全新体验
2013/01/31 HTML / CSS
世界领先的以旅馆为主的在线预订平台:Hostelworld
2016/10/09 全球购物
荷兰网上药店:Drogisterij.net
2019/09/03 全球购物
初中生三年学习生活的自我评价
2013/11/03 职场文书
党的群众教育实践活动实施方案
2014/06/12 职场文书
钢铁是怎样炼成的读书笔记
2015/06/29 职场文书
行政处罚听证告知书
2015/07/01 职场文书
用golang如何替换某个文件中的字符串
2021/04/25 Golang