JS中自定义事件与观察者模式详解

目录
  • 一、前言
  • 二、观察者模式优缺点
  • 三、代码实现
  • 四、DOM自定义事件API

一、前言

观察者模式 也称发布-订阅模式 、 模型-视图模式 。当对象间存在一对多关系时,则使用观察者模式(Observer Pattern)。比如,当一个对象被修改时,则会自动通知依赖它的对象。观察者模式属于行为型模式。

观察者模式: 类似我们在微信平台订阅了公众号 , 当它有新的文章发表后,就会推送给我们所有订阅的人。

我们作为订阅者不必每次都去查看这个公众号有没有新文章发布,公众号作为发布者会在合适时间通知我们。

我们与公众号之间不再强耦合在一起。公众号不关心谁订阅了它, 不管你是男是女还是宠物狗,它只需要定时向所有订阅者发布消息即可。

二、观察者模式优缺点

优点:

  • 可以广泛应用于异步编程,它可以代替我们传统的回调函数。
  • 我们不需要关注对象在异步执行阶段的内部状态,我们只关心事件完成的时间点。
  • 取代对象之间硬编码通知机制,一个对象不必显式调用另一个对象的接口,而是松耦合的联系在一起。
  • 降低了目标与观察者之间的耦合关系,两者之间是抽象耦合关系。符合依赖倒置原则。

缺点:

  • 如果一个被观察者对象有很多的直接和间接的观察者的话,将所有的观察者都通知到会花费很多时间。
  • 如果在观察者和观察目标之间有循环依赖的话,观察目标会触发它们之间进行循环调用,可能导致系统崩溃。
  • 观察者模式没有相应的机制让观察者知道所观察的目标对象是怎么发生变化的,而仅仅只是知道观察目标发生了变化。

三、代码实现

  • Event简易版
var Event = (function() {
  let list = {};
  let listen = function(key, fn) {
    (list[key] || (list[key] = [])).push(fn);
  };
  let remove = function(key, fn) {
    let fns = list[key];
    if (!fns) return;
    if (!fn) {
      fns && (fns.length = 0);
    } else {
      for (let i = fns.length - 1; i >= 0; i--) {
        fns[i] === fn && (fns.splice(i, 1));
      }
    }
  };
  let trigger = function() {
    let keys = [].shift.call(arguments);
    let fns = list[keys];
    if (!fns || fns.length === 0) return;
    for (let i = 0, fn; fn = fns[i++];) {
      fn.apply(this, arguments);
    }
  };
  return {
    listen,
    remove,
    trigger
  };
})();
// 使用
function log(name) {
  console.log(name)
}
Event.listen('to-do', log)    // 订阅事件
Event.trigger('to-do', '111') // 111
Event.trigger('to-do', '222') // 222
Event.remove('to-do', log)    // 移除事件
Event.trigger('to-do', '333') // 不执行
  • 类似Nodejs的EventEmitter模拟版
function EventEmitter() {
  this._maxListeners = 10;
  this._events = Object.create(null);
}
// 向事件队列添加事件
// prepend为true表示向事件队列头部添加事件
EventEmitter.prototype.addListener = function (type, listener, prepend) {
  if (!this._events) {
    this._events = Object.create(null);
  }
  if (this._events[type]) {
    if (prepend) {
      this._events[type].unshift(listener);
    } else {
      this._events[type].push(listener);
    }
  } else {
    this._events[type] = [listener];
  }
};
// 移除某个事件
EventEmitter.prototype.removeListener = function (type, listener) {
  if (Array.isArray(this._events[type])) {
    if (!listener) {
      delete this._events[type]
    } else {
      this._events[type] = this._events[type].filter(e => e !== listener && e.origin !== listener)
    }
  }
};
// 向事件队列添加事件,只执行一次
EventEmitter.prototype.once = function (type, listener) {
  const only = (...args) => {
    listener.apply(this, args);
    this.removeListener(type, listener);
  }
  only.origin = listener;
  this.addListener(type, only);
};
// 执行某类事件
EventEmitter.prototype.emit = function (type, ...args) {
  if (Array.isArray(this._events[type])) {
    this._events[type].forEach(fn => {
      fn.apply(this, args);
    });
  }
};
// 设置最大事件监听个数
EventEmitter.prototype.setMaxListeners = function (count) {
  this.maxListeners = count;
};
// 使用
var emitter = new EventEmitter();
var onceListener = function (args) {
  console.log('我只能被执行一次', args, this);
}
var listener = function (args) {
  console.log('我是一个listener', args, this);
}
emitter.once('click', onceListener);
emitter.addListener('click', listener);
emitter.emit('click', '参数');
emitter.emit('click');
emitter.removeListener('click', listener);
emitter.emit('click');

四、DOM自定义事件API

  • Event构造器
// 创建
var customEvent = new Event('custom-event');
// 监听
window.addEventListener('custom-event', function(e) {
  // do something ...
  console.log(`事件对象:`, e)
})
// 触发
window.dispatchEvent(customEvent)
  • CustomEvent构造器,可传递参数。
// 创建
var customEvent = new CustomEvent('custom-event', {
  data: {
    name: 'Tom',
    age: 18,
    other: 'otherMessage',
  }
});
// 监听
var eBox = document.getElementById('box')
eBox.addEventListener('custom-event', function(e) {
  // do something ...
  console.log(`传入的数据:`, e.data)
})
// 触发 兼容写法
if(window.dispatchEvent) {
  eBox.dispatchEvent(customEvent);
} else {
  // IE8
  eBox.fireEvent(customEvent);
}

以上就是JS中自定义事件与观察者模式详解的详细内容,更多关于JS自定义事件观察者模式的资料请关注我们其它相关文章!

(0)

相关推荐

  • javascript自定义事件功能与用法实例分析

    本文实例讲述了javascript自定义事件功能与用法.分享给大家供大家参考,具体如下: 概述 自定义事件很难派上用场? 为什么自定义事件很难派上用场,因为以前js不是模块化开发,也很少协作.因为事件本质是一种通信方式,是一种消息,只有存在多个对象,多个模块的情况下,才有可能需要用到事件进行通信.而现在有了模块化之后,已经可以使用自定义事件进行各模块间协作了. 哪里用得到自定义事件? 事件本质是一种消息,事件模式本质上是观察者模式的实现,那么用得上观察者模式的地方,自然也可以也可以用上事件模式.

  • js观察者模式的介绍及使用

    目录 一.定义 二.使用场景 三.举个例子 四.Coding 一.定义 观察者模式定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象(通知者).这个主题对象观察到被观察者发生变化时,会通知所有的观察者对象,使它们能够自己更新自己 这里涉及了几个角色及他们自己功能: 观察者对象:可以更新自己 主题对象:可以添加观察者,移除观察者,通知观察者 被观察者:被主题对象监视,当被观察者发生变化时,主题对象会通知观察者更新自己的状态 二.使用场景 当一个对象改变需要同时改变其他对象的时候,而

  • 利用Javascript实现一套自定义事件机制

    前言 事件机制为我们的web开发提供了极大的方便,使得我们能在任意时候指定在什么操作时做什么操作.执行什么样的代码. 如点击事件,用户点击时触发:keydown.keyup事件,键盘按下.键盘弹起时触发:还有上传控件中,文件加入前事件,上传完成后事件. 由于在恰当的时机会有相应的事件触发,我们能为这些事件指定相应的处理函数,就能在原本的流程中插入各种各样的个性化操作和处理,使得整个流程变得更加丰富. 诸如click.blur.focus等事件是原本的dom就直接提供的原生事件,而我们使用的一些其

  • JS中自定义事件的使用与触发操作实例分析

    本文实例讲述了JS中自定义事件的使用与触发操作.分享给大家供大家参考,具体如下: 1. 事件的创建 JS中,最简单的创建事件方法,是使用Event构造器: var myEvent = new Event('event_name'); 但是为了能够传递数据,就需要使用 CustomEvent 构造器: var myEvent = new CustomEvent('event_name', { detail:{ // 将需要传递的数据写在detail中,以便在EventListener中获取 //

  • JavaScript设计模式之观察者模式与发布订阅模式详解

    本文实例讲述了JavaScript设计模式之观察者模式与发布订阅模式.分享给大家供大家参考,具体如下: 学习了一段时间设计模式,当学到观察者模式和发布订阅模式的时候遇到了很大的问题,这两个模式有点类似,有点傻傻分不清楚,博客起因如此,开始对观察者和发布订阅开始了Google之旅.对整个学习过程做一个简单的记录. 观察者模式 当对象间存在一对多关系时,则使用观察者模式(Observer Pattern).比如,当一个对象被修改时,则会自动通知它的依赖对象.观察者模式属于行为型模式.在观察模式中共存

  • 手把手教你用Javascript实现观察者模式

    目录 什么是观察者模式? 场景模拟 代码实现 重构代码 总结 什么是观察者模式? 观察者模式一种设计模式. 观察者模式定义了对象间的一种 一对多 的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都将得到通知,并自动更新. 简单说明,在观察者模式中,有两个模型,一个观察者(observer)和一个被观察者(Observed).当被观察者发生改变或变化时,会通知观察者. 场景模拟 倘若即将到来双11,想要在双11购买商品的人就是观察者(Observer) 想要购买的商品就是被观察者(Obs

  • JS中自定义事件与观察者模式详解

    目录 一.前言 二.观察者模式优缺点 三.代码实现 四.DOM自定义事件API 一.前言 观察者模式 也称发布-订阅模式 . 模型-视图模式 .当对象间存在一对多关系时,则使用观察者模式(Observer Pattern).比如,当一个对象被修改时,则会自动通知依赖它的对象.观察者模式属于行为型模式. 观察者模式: 类似我们在微信平台订阅了公众号 , 当它有新的文章发表后,就会推送给我们所有订阅的人. 我们作为订阅者不必每次都去查看这个公众号有没有新文章发布,公众号作为发布者会在合适时间通知我们

  • SpringBoot实现自定义事件的方法详解

    目录 简介 步骤1:自定义事件 步骤2:自定义监听器 方案1:ApplicationListener 方案2:SmartApplicationListener 步骤3:注册监听器 法1:@Component(适用于所有监听器) 法2:application.yml中添加配置 法3:启动类中注册 步骤4:发布事件 法1:注入ApplicationContext,调用其publishEvent方法 法2:启动类中发布 简介 说明 本文用实例来介绍如何在SpringBoot中自定义事件来使用观察者模式

  • 原生js中ajax访问的实例详解

    原生js中ajax访问的实例详解 form表单中 登录名: 失去光标即触发事件 function createXmlHttp() { var xmlHttp; try { // Firefox, Opera 8.0+, Safari xmlHttp = new XMLHttpRequest(); } catch (e) { try {// Internet Explorer xmlHttp = new ActiveXObject("Msxml2.XMLHTTP"); } catch (

  • Node.Js中实现端口重用原理详解

    本文介绍了Node.Js中实现端口重用原理详解,分享给大家,具体如下: 起源,从官方实例中看多进程共用端口 const cluster = require('cluster'); const http = require('http'); const numCPUs = require('os').cpus().length; if (cluster.isMaster) { console.log(`Master ${process.pid} is running`); for (let i =

  • java 中自定义OutputFormat的实例详解

    java 中 自定义OutputFormat的实例详解 实例代码: package com.ccse.hadoop.outputformat; import java.io.IOException; import java.net.URI; import java.net.URISyntaxException; import java.util.StringTokenizer; import org.apache.hadoop.conf.Configuration; import org.apa

  • JQuery在循环中绑定事件的问题详解

    有个页面上需要N个DOM,每个DOM里面的元素ID都要以数字结尾,比如说 <input type="text" name="username" id="username_1" value="" /> <input type="text" name="username" id="username_2" value="" />

  • 在MySQL中自定义参数的使用详解

    MySQL变量包括系统变量和系统变量.这次的学习任务是用户自定义变量.用户变量主要包括局部变量和会话变量. 用户自定义变量的声明方法形如:@var_name,其中变量名称由字母.数字."."."_"和"$"组成.当然,在以字符串或者标识符引用时也可以包含其他字符(例如:@'my-var',@"my-var",或者@my-var). 用户自定义变量是会话级别的变量.其变量的作用域仅限于声明其的客户端链接.当这个客户端断开时,其所

  • js中Object.create实例用法详解

    1.用Object.create()方法创建新对象,并使用现有对象提供新对象的proto. 2.提供两个参数,第一个是新创建的原型对象,第二个是为新创建的对象添加属性的对象. 实例 // father 对象 let father = { name: 'father', friend: ['abby', 'bob'] } // 生成新实例对象 child1 let child1 = Object.create(father) // 更改值类型属性 child1.name = '修改了name' c

  • js中 javascript:void(0) 用法详解

    javascript:void(0)表示不做任何动作.如: 复制代码 代码如下: <a href="javascript:void(0);" onclick="alert('ok');"></a> 这里表示这个链接不做跳转动作,执行onClick事件. 我想使用过ajax的都常见这样的代码: 复制代码 代码如下: <a href="javascript:doTest2();void(0);">here</

随机推荐