学习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 相关文章推荐
JavaScript 不只是脚本
May 30 Javascript
javascript中删除指定数组中指定的元素的代码
Feb 12 Javascript
修改好的jquery滚动字幕效果实现代码
Jun 22 Javascript
JS 毫秒转时间示例代码
Sep 22 Javascript
AngularJS  $on、$emit和$broadcast的使用
Sep 05 Javascript
详解JS中的柯里化(currying)
Aug 17 Javascript
jQuery 实现鼠标画框并对框内数据选中的实例代码
Aug 29 jQuery
使用JavaScript实现一个小程序之99乘法表
Sep 21 Javascript
angular.js和vue.js中实现函数去抖示例(debounce)
Jan 18 Javascript
浅谈vue.js导入css库(elementUi)的方法
Mar 09 Javascript
webpack多入口多出口的实现方法
Aug 17 Javascript
JavaScript实现滚动加载更多
Dec 27 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定时自动生成静态HTML的实现代码
2010/06/20 PHP
php下使用curl模拟用户登陆的代码
2010/09/10 PHP
几个有用的php字符串过滤,转换函数代码
2012/05/01 PHP
ThinkPHP中处理表单中的注意事项
2014/11/22 PHP
php中Redis的应用--消息传递
2017/03/28 PHP
Laravel validate error处理,ajax,json示例
2019/10/25 PHP
javascript的键盘控制事件说明
2008/04/15 Javascript
jQuery 行背景颜色的交替显示(隔行变色)实现代码
2009/12/13 Javascript
javascript克隆对象深度介绍
2012/11/20 Javascript
jquery $.fn $.fx是什么意思有什么用
2013/11/04 Javascript
jqGrid读取选择的多行的某个属性代码
2014/05/18 Javascript
使用JavaScript 实现的人脸检测
2015/03/24 Javascript
Bootstrap Table使用方法详解
2016/08/01 Javascript
javascript设计模式之策略模式学习笔记
2017/02/15 Javascript
js 转义字符及URI编码详解
2017/02/28 Javascript
React-Native实现ListView组件之上拉刷新实例(iOS和Android通用)
2017/07/11 Javascript
JavaScript事件冒泡机制原理实例解析
2020/01/14 Javascript
vue搜索页开发实例代码详解(热门搜索,历史搜索,淘宝接口演示)
2020/04/11 Javascript
解决vue-router路由拦截造成死循环问题
2020/08/05 Javascript
[01:23:45]DOTA2-DPC中国联赛 正赛 CDEC vs Dragon BO3 第一场 1月22日
2021/03/11 DOTA
便捷提取python导入包的属性方法
2018/10/15 Python
python多进程使用及线程池的使用方法代码详解
2018/10/24 Python
Python随机函数库random的使用方法详解
2019/08/21 Python
解决python 文本过滤和清理问题
2019/08/28 Python
Python迭代器模块itertools使用原理解析
2019/12/11 Python
Tensorflow 实现分批量读取数据
2020/01/04 Python
Python爬虫爬取新闻资讯案例详解
2020/07/14 Python
Python中logging日志的四个等级和使用
2020/11/17 Python
会计专业应届生求职信
2013/11/24 职场文书
《灯光》教学反思
2014/02/08 职场文书
买房委托公证书
2014/04/08 职场文书
民生工作实施方案
2014/05/31 职场文书
具结保证书
2015/01/17 职场文书
2014年度个人工作总结范文
2015/03/09 职场文书
无婚姻登记记录证明
2015/06/18 职场文书
java获取一个文本文件的编码(格式)信息
2022/09/23 Java/Android