23种设计模式全面解析 -- 观察者模式

409 阅读3分钟

观察者模式

观察者(Observer)模式:多个对象间存在一对多关系,当一个对象发生改变时,把这种改变通知给其他多个对象,从而影响其他对象的行为, 比如使它们能够自动更新自己。

当一个对象的改变需要同时改变其它对象,并且它不知道具体有多少对象需要改变的时候,就应该考虑使用观察者模式。

发布 & 订阅 一对多

// 主题 保存状态, 状态变化之后触发所有观察者对象。
class Subject {
  constructor() {
    this.state = 0;
    this.observers = []
  }
  getState() {
    return this.state;
  }
  setState(state) {
    this.state = state;
    this.notifyAllObservers()
  }
  notifyAllObservers() {
    this.observers.forEach(observer => {
      observer.update()
    })
  }
  attach(observer){
    this.observers.push(observer);
  }
}
// 观察者
class Observer {
  constructor(name, subject) {
     this.name = name;
     this.subject = subject;
     this.subject.attach(this)
  }
  update() {
    console.log(`${this.name} update, state: ${this.subject.getState()}`);
  }
}
// 测试

let s = new Subject();
let o1 = new Observer('o1', s);
let o2 = new Observer('o2', s);

s.setState(12);

Nodejs的EventEmitter

Nodejs的EventEmitter就是观察者模式的典型实现,Nodejs的events模块只提供了一个对象: events.EventEmitter。EventEmitter 的核心就是事件触发事件监听器功能的封装。

  • addListener(event, listener)

为指定事件添加一个监听器,默认添加到监听器数组的尾部。

  • removeListener(event, listener)

移除指定事件的某个监听器,监听器必须是该事件已经注册过的监听器。它接受两个参数,第一个是事件名称,第二个是回调函数名称。

  • setMaxListeners(n)

默认情况下, EventEmitters 如果你添加的监听器超过 10 个就会输出警告信息。 setMaxListeners 函数用于提高监听器的默认限制的数量。

  • once(event, listener)

为指定事件注册一个单次监听器,即 监听器最多只会触发一次,触发后立刻解除该监听器。

  • emit(event, [arg1], [arg2], [...])

按监听器的顺序执行执行每个监听器,如果事件有注册监听返回 true,否则返回 false。

function EventEmitter() {
 this.maxListeners = 10
 this.events = Object.create(null);
}
//向事件队列头部添加时间
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.removeListner = 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 = functuion(type, ...args) {
  if(Array.isArray(this.events[type])) {
    this.events[type].forEach(fn=> {
      fn.apply(this, args);
    })
  }
}

// 设置最大监听个数。
EventEmitter.prototype.setMaxListenrs = function(count) {
   thihs.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');

image.png

javaScript DOM事件

document.body.addEventListener('click', function() {
    console.log('hello world!');
});
document.body.click()

//1、创建事件
const myEvent = new Event("myEvent");

// 2、注册事件监听器
elem.addEventListener("myEvent", function(e){
})
// 3、 触发事件

elem.dispatchEvent(myEvent);

vue 响应式

  • 当你把一个普通的 JavaScript 对象传入 Vue 实例作为 data 选项

  • Vue 将遍历此对象所有的 property,并使用 Object.defineProperty 把这些 property 全部转为 getter/setter

  • Object.defineProperty 是 ES5 中一个无法 shim 的特性,这也就是 Vue 不支持 IE8 以及更低版本浏览器的原因。

  • 这些 getter/setter 对用户来说是不可见的,但是在内部它们让 Vue 能够追踪依赖,在 property 被访问和修改时通知变更。

每个组件实例都对应一个 watcher 实例,它会在组件渲染的过程中把“接触”过的数据 property 记录为依赖。之后当依赖项的 setter 触发时,会通知 watcher,从而使它关联的组件重新渲染