JavaScriptは観察者モードを実現します.

5900 ワード

概念:
[wiki]観察者モードはソフトウェア設計モードの一種である.このようなモードでは、目標オブジェクトは、その観察者に依存するすべてのオブジェクトを管理し、その自身の状態が変化したときに、自動的に通知を発行する.これは、一般に、観察者によって提供される方法を呼び出して実現される.このようなモードは通常、リアルタイムイベント処理システムとして用いられる.
ES 5の実装
またES 5では主に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"}
ES 6の実現(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を使って実現
ES 6に追加されたProxyアプリは多くの用途があり、Reflect Appiと組み合わせて多くの強力な論理を簡単に実現できます.詳しくは「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("       ");
ES 5で使用する
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