学习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 插件学习(三)
Aug 06 Javascript
Java 正则表达式学习总结和一些小例子
Sep 13 Javascript
form.submit()不能提交表单的原因分析
Oct 23 Javascript
深入浅析JavaScript字符串操作方法 slice、substr、substring及其IE兼容性
Dec 16 Javascript
BootStrap智能表单demo示例详解
Jun 13 Javascript
功能强大的Bootstrap组件(结合js)
Aug 03 Javascript
Javascript数组中push方法用法分析
Oct 31 Javascript
Bootstrap实现圆角、圆形头像和响应式图片
Dec 14 Javascript
Bootstrap 3 按钮标签实例代码
Feb 21 Javascript
nuxt+axios解决前后端分离SSR的示例代码
Oct 24 Javascript
玩转Koa之核心原理分析
Dec 29 Javascript
微信小程序分享小程序码的生成(带参数)以及参数的获取
Mar 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
终于听上了直流胆调频
2021/03/02 无线电
发挥语言的威力--融合PHP与ASP
2006/10/09 PHP
PHP脚本中include文件出错解决方法
2008/11/20 PHP
PHP消息队列用法实例分析
2016/02/12 PHP
php微信公众账号开发之前五个坑(一)
2016/09/18 PHP
Thinkphp5框架使用validate实现验证功能的方法
2019/08/27 PHP
Laravel5.4框架中视图共享数据的方法详解
2019/09/05 PHP
11个用于提高排版水平的基于jquery的文字效果插件
2012/09/14 Javascript
javascript 实现 秒杀,团购 倒计时展示的记录 分享
2013/07/12 Javascript
JavaScript实现从数组中选出和等于固定值的n个数
2014/09/03 Javascript
JQuery 传送中文乱码问题的简单解决办法
2016/05/24 Javascript
js实现弹窗居中的简单实例
2016/10/09 Javascript
JS实现的自动打字效果示例
2017/03/10 Javascript
vue2.0实战之基础入门(1)
2017/03/27 Javascript
AngularJS封装$http.post()实例详解
2017/05/06 Javascript
nodejs简单抓包工具使用详解
2019/08/23 NodeJs
vue点击自增和求和的实例代码
2019/11/06 Javascript
Python 文件重命名工具代码
2009/07/26 Python
Python随机数random模块使用指南
2016/09/09 Python
利用Celery实现Django博客PV统计功能详解
2017/05/08 Python
python 爬虫一键爬取 淘宝天猫宝贝页面主图颜色图和详情图的教程
2018/05/22 Python
在Python 不同级目录之间模块的调用方法
2019/01/19 Python
如何用Python做一个微信机器人自动拉群
2019/07/03 Python
python将音频进行变速的操作方法
2020/04/08 Python
Python自动化之UnitTest框架实战记录
2020/09/08 Python
python编程的核心知识点总结
2021/02/08 Python
HTML5中canvas中的beginPath()和closePath()的重要性
2018/08/24 HTML / CSS
阿迪达斯荷兰官方网站:adidas荷兰
2018/03/16 全球购物
水上运动奥特莱斯:Wasterports Outlet
2018/08/08 全球购物
Europcar德国:全球汽车租赁领域的领导者
2018/08/15 全球购物
REISS美国官网:伦敦最受欢迎的时尚品牌
2019/08/16 全球购物
Skyscanner新西兰:全球领先的旅游搜索网站
2019/08/26 全球购物
祖国在我心中演讲稿
2014/01/15 职场文书
父母对孩子的寄语
2014/04/09 职场文书
教师求职自荐信
2015/03/26 职场文书
Go语言安装并操作redis的go-redis库
2022/04/14 Golang