概念:
[wiki] 观察者模式是软件设计模式的一种。在此种模式中,一个目标对象管理所有相依于它的观察者对象,并且在它本身的状态改变时主动发出通知。这通常透过呼叫各观察者所提供的方法来实现。此种模式通常被用来实时事件处理系统。
ES5下的实现
再ES5中主要是通过Object.defineProperties
方法定义对象属性的设置(set)和获取(get),并再进行设置时执行相关的处理函数,如下:
var targetObj={
age:1
}
function observer(oldval,newval){
console.log('name属性的值从 '+oldval+'改变为 '+newval);
}
Object.defineProperty(targetObj,'name',{
enumerable:true,
configurable:true,
get:function(){
return name;
},
set:function(val){
//调用处理函数
observer(name,val);
name=val;
}
});
targetObj.name="www";
targetObj.name="mmm";
console.info('targetObj:',targetObj);
结果为:
name属性的值从 改变为 www
name属性的值从 www改变为 mmm
targetObj:{age:1,name:"mmm"}
ES6的实现(使用set方法实现)
class TargetObj{
constructor(age,name){
this.name=name;
this.age=age;
}
set name(val){
Observer(name,val);
name=val;
}
}
function Observer(oldval,newval){
console.info('name属性的值从 '+ oldval +' 改变为 ' + newval);
}
let targetObj2 = new TargetObj(1,'www');
targetObj2.name="mmm";
console.info(targetObj2);
使用Reflect和Proxy实现
在ES6中新增的Proxy Api用处很多,结合Reflect Api可以方便的实现很多强大的逻辑,详细介绍可以参见《ECMAScript 6 入门》—— 阮一峰 中的介绍。实现代码如下:
class TargetObj {
constructor(age, name) {
this.name = name;
this.age = age;
}
}
let targetObj = new TargetObj(1, "www");
let observerProxy = new Proxy(targetObj, {
set(target, property, value, reciever) {
if (property === "name") {
observer(target[property], value);
}
Reflect.set(target, property, value, reciever);
}
});
function observer(oldval, newval) {
console.info(`name属性的值从${oldval} 改变为${newval}`);
}
observerProxy.name="mmm";
console.info(targetObj);
结果为:
name属性的值从www 改变为mmm
TargetObj {name: "mmm", age: 1}
通用观察者模式
完整实现
let Observer = (function() {
// 防止消息队列暴露而被篡改,故将消息容器作为静态私有变量保存
var __messages = {};
return {
regist: function regist(type, fn) {
//如果此消息不存在则应该创建一个该消息类型
if (typeof __messages[type] === "undefined") {
// 将动作推入到该消息对应的动作执行队列中
__messages[type] = [fn];
} else {
// 将动作方法推入该消息对应的动作执行序列中
__messages[type].push(fn);
}
return this;
},
fire: function fire(type, args) {
// 如果该消息没有被注册,则返回
if (!__messages[type]) return;
// 定义消息信息
var events = {
type: type,
args: args || {}
};
var i = 0;
var len = __messages[type].length;
// 遍历消息动作
for (; i < len; i++) {
// 依次执行注册的消息对应的动作序列
__messages[type][i].call(this, events);
}
return this;
},
remove: function remove(type, fn) {
// 如果消息动作队列存在
if (__messages[type] instanceof Array) {
// 从最后一个消息动作遍历
var i = __messages[type].length - 1;
for (; i >= 0; i--) {
// 如果存在该动作则在消息动作序列中一处相应动作
__messages[type][i] === fn && __messages[type].splice(i, 1);
}
}
}
};
})();
简单的使用
const observerFns = {
test: "test",
addUser: "addUser"
};
Observer.regist(observerFns.test, e => {
console.log(e.type, e.args.msg);
})
.regist(observerFns.test, e => {
console.info(e.type, e.args.msg);
})
.regist(observerFns.addUser, e => {
var type = e.type;
var args = e.args;
console.log(args);
``;
});
Observer.fire(observerFns.test, { msg: "这是我传的参数" });
Observer.fire(observerFns.addUser, { name: "wwm" });
结果
test 这是我传的参数
test 这是我传的参数
Object {name: "wwm"}
用类实现方法的自动调用
class Student {
result: string;
constructor(result: string) {
this.result = result;
this.say = this.say.bind(this); // 解决`class`的方法单独使用时`this`指向问题
}
say(e) {
console.log(this.result);
}
answer(question: string) {
// 注册回答问题
Observer.regist(question, this.say);
}
sleep(question: string) {
console.log(this.result + " " + question + " 已被注销");
Observer.remove(question, this.say);
}
}
class Teacher {
ask(question) {
console.log("问题是: " + question);
Observer.fire(question, question);
}
}
var student1 = new Student("学生1回答问题");
var student2 = new Student("学生2回答问题");
var student3 = new Student("学生3回答问题");
var teacher = new Teacher();
student1.answer("什么是设计模式");
student1.answer("简述观察者模式");
student2.answer("什么是设计模式");
student3.answer("简述观察者模式");
student3.sleep("什么是设计模式");
teacher.ask("什么是设计模式");
teacher.ask("简述观察者模式");
在ES5中使用
var Student = function(result) {
var that = this;
that.result = result;
that.say = function() {
console.log(that.result);
};
};
Student.prototype.answer=function(question){
Observer.regist(question,this.say)
}
Student.prototype.sleep=function(question){
Observer.remove(question,this.say)
}
var Teacher=function(){};
Teacher.prototype.ask=function(question){
console.log("问题是: "+question);
Observer.fire(question,null)
}
var student1 = new Student("学生1回答问题");
var student2 = new Student("学生2回答问题");
var student3 = new Student("学生3回答问题");
var teacher = new Teacher();
student1.answer("什么是设计模式");
student1.answer("简述观察者模式");
student2.answer("什么是设计模式");
student3.answer("简述观察者模式");
student3.sleep("什么是设计模式");
teacher.ask("什么是设计模式");
teacher.ask("简述观察者模式");
输出
学生3回答问题 什么是设计模式 已被注销
问题是: 什么是设计模式
学生1回答问题
学生2回答问题
问题是: 简述观察者模式
学生1回答问题
学生3回答问题