当前位置: 首页 > article >正文

JavaScript 观察者设计模式

观察者模式:观察者模式(Observer mode)指的是函数自动观察数据对象,一旦对象有变化,函数就会自动执行。而js中最常见的观察者模式就是事件触发机制。

ES5/ES6实现观察者模式(自定义事件) - 简书

先搭架子

  1. 要有一个对象,存储着它自己的触发函数。而且这个对象的触发函数可能有很多种,比如一个onclick可能触发多个事件,那么handler的属性应该是一个数组,每个数组的值都是一个函数。
handler={
  type1:[func1,func2...],
  type2:[func3,func4...],
  ...
}
  1. 现在这个对象的主体部分已经思考好了,现在就是要它‘动起来’,给它添加各种动作。
    一个事件可能有哪些动作呢?
  • add:添加事件某种类型的函数,
  • remove: 移除某种类型的函数,
  • fire:触发某种类型的函数,
  • once:触发某种类型的函数,然后移除掉这个函数
  1. 现在,自定义事件的架子已经搭建好了
eventOb={
  //函数储存
  handler:{
    type1:[func1,func2...],
    type2:[func2,func4...],
    ...
  },

  //主要事件
  add:function(){},
  remove:function(){},
  fire:function(){},
  once:function(){},
}

add

添加一个事件监听,首先传入参数应该是 事件类型type,和触发函数 func,传入的时候检测有没有这个函数,有了就不重复添加。

add:function (type,func) {
  //检测type是否存在
  if(eventOb.handleFunc[type]){
    //检测事件是否存在,不存在则添加
    if(eventOb.handleFunc[type].indexOf(func)===-1){
      eventOb.handleFunc[type].push(func);
    }
  }
  else{
    eventOb.handleFunc[type]=[func];
  }
},

remove

remove有一个潜在的需求,就是如果你的事件不存在,它应该会报错。而这里不会报错,index在func不存在的时候是-1;这时候要报错。

remove:function (type,func) {
  try{
    let target = eventOb.handleFunc[type];
    let index = target.indexOf(func);
    if(index===-1) throw error;
    target.splice(index,1);
  }catch (e){
      console.error('别老想搞什么飞机,删除我有的东西!');
  }
},

fire

触发一个点击事件肯定是要触发它全部的函数,这里也是一样,所以只需要传入type,然后事件可能不存在,像上面一样处理。

fire:function (type,func) {
  try{
    let target = eventOb.handleFunc[type];
    let count = target.length;
    for (var i = 0; i < count; i++) {
      //加()使立即执行
      target[i]();
    }    
  }
  catch (e){
    console.error('别老想搞什么飞机,触发我有的东西!');
  }
},

once

fire,然后remove?

但会有问题,我只想触发并且删除某个事件怎么办,fire一下就全触发了呀。
所以fire的问题就显现出来了。我们还是要给它一个func,但是可选。

fire:function (type,func) {
  try{
    let target = eventOb.handleFunc[type];
    if(arguments.length===1) {
      //不传func则全部触发
      let count = target.length;
      for (var i = 0; i < count; i++) {
          target[i]();
      }
    }else{
      //传func则触发func
      let index=target.indexOf(func);
      if(index===-1)throw error;
      func();
    }
    //need some code
  }catch (e){
    console.error('别老想搞什么飞机,触发我有的东西!');
    //need some code
  }
},

完整代码

class eventObs {
  constructor(){
    this.handleFunc={}
  }

  add(type,func){
    if(this.handleFunc[type]){
        if(this.handleFunc[type].indexOf(func)===-1){
            this.handleFunc[type].push(func);
        }
    }else{
        this.handleFunc[type]=[func];
    }
  };

  fire(type,func){
    try{
      if(arguments.length===1) {
          let target = this.handleFunc[type];
          let count = target.length;
          for (var i = 0; i < count; i++) {
              target[i]();
          }
      }else{
          let target = this.handleFunc[type];
          let index=target.indexOf(func);
          if(index===-1)throw error;
          func();
      }
      return true;
    }catch (e){
        console.error('别老想搞什么飞机,触发我有的东西!');
        return false;
    }
  };

  remove(type,func){
      try{
          let target = this.handleFunc[type];
          let index=target.indexOf(func);
          if(index===-1)throw error;
          target.splice(index,1);
      }catch (e){
          console.error('别老想搞什么飞机,删除我有的东西!');
      }

  };

  once(type,func) {

    this.fire(type, func)
    ? this.remove(type, func)
    : null;
  }
}

使用ES6的Reflect来实现一个观察者模式

[!NOTE]
函数(观察者)自动观察数据对象(观察的目标),一旦对象发生变化,函数就会自动执行

    // 观察者设计模式
    const queuedObservers = new Set();
    
    const observe = fn => queuedObservers.add(fn);
    const observable = obj => new Proxy(obj, {set});
    
    function set(target, key, value, receiver) {
      const result = Reflect.set(target, key, value, receiver);
      queuedObservers.forEach(observer => observer());
      return result;
    }
    
    
    // test
    const person = observable({
      name: '张三',
      age: 20
    });
    
    function print() {
      console.log(`${person.name}, ${person.age}`)
    }
    
    observe(print);
    person.name = '李四';
    // 输出
    // 李四, 20

http://www.kler.cn/a/392095.html

相关文章:

  • 若依笔记(八):芋道的Docker容器化部署
  • 使用 Visual Studio Installer 彻底卸载 Visual Studio方法与下载
  • 【go从零单排】Timer、Epoch 时间函数
  • Java设计模式面试题及参考答案
  • SciPy:Python 科学计算工具包的全面教程
  • Kubernetes在容器编排中的应用
  • Scala的List(可变)
  • 微搭低代码入门02条件语句
  • 【SpringBoot】黑马大事件笔记-day3
  • 用 Python 从零开始创建神经网络(二):第一个神经元的进阶
  • 停车共享小程序ssm+论文源码调试讲解
  • 实现linux定时备份数据至群晖NAS
  • python爬取newbing每日壁纸
  • JDBC事务管理、四大特征(ACID)、事务提交与回滚、MySQL事务管理
  • C语言串讲-2之指针和结构体
  • 2024 ECCV | DualDn: 通过可微ISP进行双域去噪
  • ubuntu20.04 解决Pycharm没有写入权限,无法通过检查更新更新的问题
  • k8s中基于overlay网络和underlay网络的网络插件分别有哪些
  • ima.copilot-腾讯智能工作台
  • react 中 FC 模块作用
  • int溢出值(c基础)
  • next中服务端组件共享接口数据
  • 基于yolov8、yolov5的番茄成熟度检测识别系统(含UI界面、训练好的模型、Python代码、数据集)
  • 2025年使用 AI 识别解决 reCAPTCHA
  • spring-IOC使用注解
  • Python的面向对象