美文网首页
JavaScript实现观察者模式

JavaScript实现观察者模式

作者: wwmin_ | 来源:发表于2018-04-14 23:12 被阅读29次

概念:

[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回答问题

相关文章

网友评论

      本文标题:JavaScript实现观察者模式

      本文链接:https://www.haomeiwen.com/subject/temlkftx.html