学习JavaScript设计模式之观察者模式_javascript技巧

一、定义

观察者模式(发布-订阅模式):其定义对象间一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都将得到通知。
在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
, 设计模式
观察者模式
,以便于您获取更多的相关知识。

时间: 2024-08-25 05:26:35

学习JavaScript设计模式之观察者模式_javascript技巧的相关文章

学习JavaScript设计模式之单例模式_javascript技巧

一.定义 保证一个类仅有一个实例,并提供一个访问它的全局访问点. 当单击登陆按钮,页面中出现一个登陆浮窗,这个登陆浮窗是唯一的,无论单击多少次登陆按钮,这个浮窗都只会被创建一次,那么这个登陆浮窗就适合用单例模式来创建. 二.实现原理 要实现单例并不复杂,使用一个变量来标志当前是否已经为某个类创建过对象,如果是,则在下一次获取该类的实例时,直接返回之前创建的对象. 三.假单例 全局变量不是单例模式,但在JavaScript开发中,我们经常会把全局变量当成单例来使用. var a = {}; 降低全

学习JavaScript设计模式(代理模式)_javascript技巧

代理模式是为一个对象提供一个代用品或占位符,以便控制对它的访问 代理模式的用处(个人理解):为了保障当前对象的单一职责(相对独立性),而需要创建另一个对象来处理调用当前对象之前的一些逻辑以提高代码的效率.状态判断等. 代理模式中最常用的是虚拟代理和缓存代理 一.虚拟代理 虚拟代理是把一些开销很大的对象,延迟到真正需要它的时候才去创建执行 示例: 虚拟代理实现图片预加载 // 图片加载函数 var myImage = (function(){ var imgNode = document.crea

学习JavaScript设计模式(策略模式)_javascript技巧

何为策略?比如我们要去某个地方旅游,可以根据具体的实际情况来选择出行的线路.1.策略模式的定义 如果没有时间但是不在乎钱,可以选择坐飞机. 如果没有钱,可以选择坐大巴或者火车. 如果再穷一点,可以选择骑自行车. 在程序设计中,我们也常常遇到类似的情况,要实现某一个功能有多种方案可以选择.比如一个压缩文件的程序,既可以选择zip算法,也可以选择gzip算法. 定义:策略模式定义一系列的算法,分别封装起来,让他们之间可以互相替换,此模式让算法的变化独立于使用算饭的客户. 策略模式有着广泛的应用.本节

Javascript 模式实例 观察者模式_javascript技巧

在园里有许多牛人都已经写过这些文章,不过大多的例子都是.NET,今天我要举一个JS的使用实例.有兴趣的朋友可以先了解一下一些牛人的,比如李会军大哥设计模式篇中观察者的那一节 http://www.cnblogs.com/Terrylee/archive/2006/10/23/Observer_Pattern.html说说我自己对这模式的理解,其思想核心是:每个被观察对象都依据对象数据的改变而改变,被观察对象一定要有同样的改变行为来约束,这个约束是被观察对象提供给观察器的统一接口.观察器会开发改变

仅9张思维导图帮你轻松学习Javascript 就这么简单_javascript技巧

学习的道路就是要不断的总结归纳,好记性不如烂笔头,so,下面将分享收集的 9 张 javascript 相关的思维导图(非原创). 思维导图小tips: 思维导图又叫心智图,是表达发射性思维的有效的图形思维工具 ,它简单却又极其有效,是一种革命性的思维工具.思维导图运用图文并重的技巧,把各级主题的关系用相互隶属与相关的层级图表现出来,把主题关键词与图像.颜色等建立记忆链接,思维导图充分运用左右脑的机能,利用记忆.阅读.思维的规律,协助人们在科学与艺术.逻辑与想象之间平衡发展,从而开启人类大脑的无

跟我学习javascript的严格模式_javascript技巧

一.概述 除了正常运行模式,ECMAscript 5添加了第二种运行模式:"严格模式"(strict mode).顾名思义,这种模式使得Javascript在更严格的条件下运行. 设立"严格模式"的目的,主要有以下几个: 消除Javascript语法的一些不合理.不严谨之处,减少一些怪异行为; 消除代码运行的一些不安全之处,保证代码运行的安全: 提高编译器效率,增加运行速度: 为未来新版本的Javascript做好铺垫. "严格模式"体现了Jav

跟我学习javascript的浮点数精度_javascript技巧

大多数编程语言都有几种数值型数据类型,但是JavaScript却只有一种.你可以使用typeof 运算符查看数字的类型.不管是整数还是浮点数,JavaScript都将它们简单地归类为数字. typeof 17; //number typeof 98.6; //number typeof -21.3; //number 事实上,JavaScript中所有的数字都是双精度浮点数.这是由IEEE754标准制定的64位编码数字--即"doubles".如果这一事实使你疑惑JavaScript是

javascript设计模式 接口介绍_javascript技巧

这本书中第一个重要的内容就是接口. 大家对接口应该都不陌生,简单的说接口就是一个契约或者规范.在强类型的面相对象语言中,接口可以很容易的实现.但是在javascript中并没有原生的创建或者实现接口的方式,或者判定一个类型是否实现了某个接口,我们只能利用js的灵活性的特点,模拟接口. 在javascript中实现接口有三种方式:注释描述.属性验证.鸭子模型. note:因为我看的是英文书,翻译水平有限,不知道有些词汇如何翻译,大家只能领会精神了. 1. 注释描述 (Describing Inte

学习JavaScript鼠标响应事件_javascript技巧

本文为大家分享了一个简单的鼠标模拟案例,供大家参考,具体实现内容如下 如何实现捕抓鼠标事件,当鼠标滑动时,获取当前鼠标的坐标,接着在一个透明区域里绑定捕抓的位移,这样就能在模拟的透明区域里实现鼠标滑动的模型. 效果图: HTML代码: <!DOCTYPE html> <html > <head> <meta charset="UTF-8"> <title> the mouse </title> <link r