11-发布订阅模式

95 阅读1分钟

设计模式

设计模式: 一种编程的思想,我们会基于一些代码把思想实现出来,每种设计模式都是解决一类问题,而且问题偏向于"更好的去管理代码"

发布订阅模式

发布订阅设计模式: subscribe & publish 思路一: 全局只有一个自定义池,基于自定义事件的名称来区分要执行的方法 思路二: 基于面向对象管理,每一次new 执行都单独创建一个自定义事件池,实例可以调用: on/off/emit

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
    <style>
      * {
        margin: 0;
        padding: 0;
      }
      body {
        width: 100vw;
        height: 100vh;
        background-color: aqua;
      }
    </style>
  </head>
  <body>
    <script src="./index.js"></script>
  </body>
</html>
const $sub = (function() {
  // 自定义事件池
  let listeners = {};

  // 向事件池i面加入方法
  const on = function(name, func) {
    let arr = listeners[name];
    if(!arr) {
      // 事件池中加一个自定义的事件
      listeners[name] = [func];
      return;
    }
    // 之前存在这个自定义事件,则方法储存到数组中:去重处理
    if(arr.indexOf(func) > -1) return;
    arr.push(func);
  }

  // 从事件池移除方法
  const off = function(name, func) {
    let arr = listeners[name],
        index;
    if(!arr) return;
    index = arr.indexOf(func);
    if(index === -1) return;
    // arr.splice(index, 1);//会诞生数组塌陷
    arr[index] = null;
  }

  // 通知事件池中的方法执行
  const emit = function(name, ...params) {
    let arr = listeners[name];
    if(!arr) return;
    for(let i = 0; i < arr.length; i++) {
      let item = arr[i];
      if(typeof item !== "function") {
        // 如果当前的不是函数,则把它移除掉
        arr.splice(i, 1);
        listeners[name] = arr;
        i--;
        continue;
      }
      item(...params);
    }
  }

  return {
    on,
    off,
    emit
  }
})();

const fn1 = function(x, y) {
  console.log('fn1', x + y);
}
$sub.on('@A', fn1);

const fn2 = function fn2(x, y) {
  console.log('fn2', x + y);
  $sub.off('@A', fn1);
  $sub.off('@A', fn2);
}
$sub.on('@A', fn2);

const fn3 = () => console.log("fn3")
$sub.on('@A', fn3);

const fn4 = () => console.log("fn4")
$sub.on('@A', fn4);

const fn5 = () => console.log("fn5")
$sub.on('@A', fn5);

document.body.onclick = function() {
  $sub.emit('@A', 10, 20);
}

image.png

思路二

class Subscribe {
  constructor() {
    this.listeners = {}
  }

  on(name, func) {
    let arr = this.listeners[name];
    if(!arr) {
      // 事件池中加一个自定义的事件
      this.listeners[name] = [func];
      return;
    }
    // 之前存在这个自定义事件,则方法储存到数组中:去重处理
    if(arr.indexOf(func) > -1) return;
    arr.push(func);
  }

  off(name, func) {
    let arr = this.listeners[name],
        index;
    if(!arr) return;
    index = arr.indexOf(func);
    if(index === -1) return;
    // arr.splice(index, 1);//会诞生数组塌陷
    arr[index] = null;
  }

  emit(name, ...params) {
    let arr = this.listeners[name];
    if(!arr) return;
    for(let i = 0; i < arr.length; i++) {
      let item = arr[i];
      if(typeof item !== "function") {
        // 如果当前的不是函数,则把它移除掉
        arr.splice(i, 1);
        i--;
        continue;
      }
      item(...params);
    }
  }
}

const $sub = new Subscribe();
const fn1 = function(x, y) {
  console.log('fn1', x + y);
}
$sub.on('@A', fn1);

const fn2 = function fn2(x, y) {
  console.log('fn2', x + y);
  $sub.off('@A', fn1);
  $sub.off('@A', fn2);
}
$sub.on('@A', fn2);

const fn3 = () => console.log("fn3")
$sub.on('@A', fn3);

const fn4 = () => console.log("fn4")
$sub.on('@A', fn4);

const fn5 = () => console.log("fn5")
$sub.on('@A', fn5);

document.body.onclick = function() {
  $sub.emit('@A', 10, 20);
}

image.png