学习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 相关文章推荐
Sample script that displays all of the users in a given SQL Server DB
Jun 16 Javascript
效率高的Javscript字符串替换函数的benchmark
Aug 02 Javascript
判断脚本加载是否完成的方法
May 26 Javascript
jQuery学习2 选择器的使用说明
Feb 07 Javascript
基于JQuery实现异步刷新的代码(转载)
Mar 29 Javascript
JavaScript 更严格的相等 [译]
Sep 20 Javascript
基于javascript实现漂亮的页面过渡动画效果附源码下载
Oct 26 Javascript
第九章之路径分页标签与徽章组件
Apr 25 Javascript
vue中appear的用法
Aug 17 Javascript
Vue使用.sync 实现父子组件的双向绑定数据问题
Apr 04 Javascript
vue百度地图 + 定位的详解
May 13 Javascript
vue组件开发之tab切换组件使用详解
Aug 21 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实现显示照片exif信息的方法
2014/07/11 PHP
PHP循环遍历数组的3种方法list()、each()和while总结
2014/11/19 PHP
PHP+apc+ajax实现的ajax_upload上传进度条代码
2016/01/25 PHP
PHP的mysqli_ssl_set()函数讲解
2019/01/23 PHP
DD_belatedPNG,IE6下PNG透明解决方案(国外)
2010/12/06 Javascript
详谈javascript中DOM的基本属性
2015/02/26 Javascript
JQuery自动触发事件的方法
2015/06/13 Javascript
快速学习jQuery插件 Cookie插件使用方法
2015/12/01 Javascript
jquery选择器中的空格与大于号&gt;、加号+与波浪号~的区别介绍
2016/06/24 Javascript
JavaScript 链式结构序列化详解
2016/09/30 Javascript
jquery操作ID带有变量的节点实例
2016/12/07 Javascript
Es6 写的文件import 起来解决方案详解
2016/12/13 Javascript
微信小程序 HTTPS报错整理常见问题及解决方案
2016/12/14 Javascript
关于JS与jQuery中的文档加载问题
2017/08/22 jQuery
Bootstrap图片轮播效果详解
2017/10/17 Javascript
jQuery获取随机颜色的实例代码
2018/05/21 jQuery
javascript设计模式 ? 简单工厂模式原理与应用实例分析
2020/04/09 Javascript
vue浏览器返回监听的具体步骤
2021/02/03 Vue.js
9种python web 程序的部署方式小结
2014/06/30 Python
Python中字典的基本知识初步介绍
2015/05/21 Python
Python3读取UTF-8文件及统计文件行数的方法
2015/05/22 Python
详解Django中的form库的使用
2015/07/18 Python
常见的python正则用法实例讲解
2016/06/21 Python
Django原生sql也能使用Paginator分页的示例代码
2017/11/15 Python
基于DATAFRAME中元素的读取与修改方法
2018/06/08 Python
处理python中多线程与多进程中的数据共享问题
2019/07/28 Python
详解python内置常用高阶函数(列出了5个常用的)
2020/02/21 Python
python生成13位或16位时间戳以及反向解析时间戳的实例
2020/03/03 Python
CSS3中的Transition过度与Animation动画属性使用要点
2016/05/20 HTML / CSS
HTML5标签大全
2016/11/23 HTML / CSS
什么是设计模式
2012/06/17 面试题
管理信息系学生的自我评价
2014/01/11 职场文书
行政执法队伍作风整顿个人剖析材料
2014/10/11 职场文书
学习焦裕禄精神践行三严三实心得体会
2014/10/13 职场文书
2014年技术部工作总结
2014/12/12 职场文书
幼儿园迎新生欢迎词
2015/09/30 职场文书