单例模式

单例模式是指每一个类只有一个实例,并提供一个访问这个实例的全局访问点

//单例模式的不透明实现
// 定义一个类
function Singleton(name) {
    this.name = name;
    this.instance = null;
}
// 原型扩展类的一个方法getName()
Singleton.prototype.getName = function() {
    console.log(this.name)
};
// 获取类的实例
Singleton.getInstance = function(name) {
    if(!this.instance) {
        this.instance = new Singleton(name);
    }
    return this.instance
};

// 获取对象1
var a = Singleton.getInstance('a');
// 获取对象2
var b = Singleton.getInstance('b');
// 进行比较
console.log(a === b);

通过在一个类中包含一个变量记录这个类是否创造过实例判断是否是单例,这种实现方法的问题是,与平常使用new创造实例的用法差距较大,不了解的人不会使用,同时new还是可以创造出其他实例

//实现单例模式 (透明的)
var Singleton = (function(){
    var instance;
    var CreateSingleton = function (name) {
        this.name = name;

        if(instance) {
            return instance;
        }
        // 打印实例名字
        this.getName();

        // instance = this;
        // return instance;
        return instance = this;
    }
    // 获取实例的名字
    CreateSingleton.prototype.getName = function() {
        console.log(this.name)
    }

    return CreateSingleton;
})();
// 创建实例对象1
var a = new Singleton('a');
// 创建实例对象2
var b = new Singleton('b');

console.log(a===b)

代码中有几个需要注意的点

  1. Singleton是一个立刻执行函数,则他实际上就是这个函数执行之后的返回值CreateSingleton函数
  2. CreateSingleton函数是一个闭包,他在内部引用了instance变量,那么这个变量就不会被释放,同时因为作用域链的原因,在CreateSingleton函数内部没有发现instance变量就会去词法层面上的父级作用域中寻找,找到了instance变量并进行调用
  3. 在构造函数中this的指向,我们知道new关键词会让this指向这个变量,但是在构造函数内部时,this的指向实际是正在创建的这个实例,最后如果有return就将return的结果赋值给变量,没有return就会将目前这个对象赋值给变量。所以代码中的this.name实际是实例的名字,而instance通过作用域链访问到父级作用域中最后返回instance作为变量

发布订阅模式

发布-订阅模式其实是一种对象间一对多的依赖关系,当一个对象的状态发送改变时,所有依赖于它的对象都将得到状态改变的通知。

订阅者(Subscriber)把自己想订阅的事件注册(Subscribe)到调度中心(Event Channel),当发布者(Publisher)发布该事件(Publish Event)到调度中心,也就是该事件触发时,由调度中心统一调度(Fire Event)订阅者注册到调度中心的处理代码

class EventEmitter {
  constructor() {
      // 缓存列表
      this.listener = {}
  }

  // 订阅
  on(eventName, fn) {
      // 如果对象中没有对应的 event 值,也就是说明没有订阅过,就给 event 创建个缓存列表
      // 如有对象中有相应的 event 值,把 fn 添加到对应 event 的缓存列表里
      if(!this.listener[eventName]){
          this.listener[eventName] = [];
      }
      this.listener[eventName].push(fn);
  }

  // 取消订阅
  off(eventName, fn) {
      let callbacks = this.listener[eventName];
      // 缓存列表中没有对应的fn,返回false
      if(!callbacks){
          return false;
      }
      if(!fn){
          // 如果未传入fn,则将缓存列表中对应的fn都清空
          callbacks && (callbacks.length = 0);
      } else {
          let cb;
          // 遍历所对应的fn,判断和那个fn相同,相同则删除
          for (let i = 0, cbLen = callbacks.length; i < cbLen; i++) {
              cb = callbacks[i];
              if (cb == fn || cb.fn == fn) {
                  callbacks.splice(i, 1);
                  break
              }
          }
      }
  }

  // 监听一次
  once(eventName, fn) {
      // 先绑定,运行时删除对应的值
      let on = () => {
          this.off(eventName, on);
          fn.apply(this, arguments);
      }

      on.fn = fn;
      this.on(eventName, on);

  }

  // 发布
  emit(eventName, data) {
      const callbacks = this.listener[eventName];
      if(callbacks) {
          callbacks.forEach((c) => {
              c(data);
          })
      }
  }
}

观察者模式

定义了对象间一种一对多的依赖关系,当目标对象 Subject 的状态发生改变时,所有依赖它的对象 Observer 都会得到通知。

  1. 一个目标者对象 Subject,拥有方法:添加 / 删除 / 通知 Observer
  2. 多个观察者对象 Observer,拥有方法:接收 Subject 状态变更通知并处理;
  3. 目标对象 Subject 状态变更时,通知所有 Observer
class Subject {
  constructor() {
    this.observers = [];  // 观察者列表
  }
  // 添加
  add(observer) {
    this.observers.push(observer);
  }
  // 删除
  remove(observer) {
    let idx = this.observers.findIndex(item => item === observer);
    idx > -1 && this.observers.splice(idx, 1);
  }
  // 通知
  notify() {
    for (let observer of this.observers) {
      observer.update();
    }
  }
}

// 观察者类
class Observer {
  constructor(name) {
    this.name = name;
  }
  // 目标对象更新时触发的回调
  update() {
    console.log(`目标者通知我更新了,我是:${this.name}`);
  }
}

// 实例化目标者
let subject = new Subject();

观察者模式就是在subject中添加观察者的名单,在subject发生变化的时候就会通知列表中的观察者,观察者在内部决定要进行的操作

image-20220228153903909

发布订阅模式和观察者模式之间的区别在于发布订阅模式有一个中间的调度中心进行任务的分配,所以相对来说发布订阅模式解耦性更强