观察者模式
观察者(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');
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,从而使它关联的组件重新渲染。