2020-9-16日:高级技巧:观察者模式 - 自定义事件 ;

  • 一、自定义事件【 观察者模式 】 ; 背后的概念:创建一个管理事件的对象,让其他对象监听那些事件;
  • 组成:
  1. 主体 : 负责发布事件 ;
  2. 观察者 :通过订阅这些事件来 观察该主体 ;
  • 主要概念: 【 类比 Dom绑定事件,DOM元素便是主体,你的事件处理代码便是观察者;】
  1. 主体并不知道观察者的任何事情,也就是说它可以独自存在并正 常运作即使观察者不存在 ;
  2. ,观察者知道主体并能注册事件的回调函数(事件处理程序)。 
  • 示例 :
  1. EventTarget 类型;有一个单独的属性 handlers ,储存事件处理程序;
  2. addHandler(),用于注册给定类型事件的事件处理程序;
  3. fire(),用于触发一个事件 ;
  4. removeHandler(),用于注销某个事件类型的事件处理程序; 
  • function EventTarget() {
            this.handlers = {};
        }
    
        EventTarget.prototype = {
            constructor: EventTarget,
            addHandler: function (type, handler) {
                if (typeof this.handlers[type] == "undefined") {
                    this.handlers[type] = [];
                }
                this.handlers[type].push(handler);
            },
    
            fire: function (event) {
                if (!event.target) {
                    event.target = this;
                }
                if (this.handlers[event.type] instanceof Array) {
                    var handlers = this.handlers[event.type];
                    for (var i = 0, len = handlers.length; i < len; i++) {
                        handlers[i](event);
                    }
                }
            },
    
            removeHandler: function (type, handler) {
                if (this.handlers[type] instanceof Array) {
                    var handlers = this.handlers[type];
                    for (var i = 0, len = handlers.length; i < len; i++) {
                        if (handlers[i] === handler) {
                            break;
                        }
                    }
                    handlers.splice(i, 1);
                }
            }
        };

     

  • ( 1 ) : addHandler( ) 方法 ;
  • 过程解析:
  1. 当调用该方法时,会进行 一次检查,看看 handlers 属性中是否已经存在一个针对该事件类型的数组;
  2. 如果没有,则创建一个新 的
  3. 然后使用 push()将该处理程序添加到数组的末尾
  4. addHandler: function (type, handler) {
                if (typeof this.handlers[type] == "undefined") {
                    this.handlers[type] = [];
                }
                this.handlers[type].push(handler); // 事件可以添加多个,类比DOm ;
            },
  • 参数:
  1. 事件类型;
  2. 处理该事件的函数 ;

 

  • ( 2 ) :fire( )方法 ;
  • 过程解析:
  1. 先给 event 对象设置一个 target 属性,如果它尚未被指定的话 ;
  2. 然后它就 查找对应该事件类型的一组处理程序,调用各个函数,并给出 event 对象 ;
  3. 因为这些都是自定义事件, 所以 event 对象上还需要的额外信息由你自己决定 ;
  4. fire: function (event) {
                if (!event.target) {
                    event.target = this;
                }
                if (this.handlers[event.type] instanceof Array) {
                    var handlers = this.handlers[event.type];
                    for (var i = 0, len = handlers.length; i < len; i++) {
                        handlers[i](event);  // 循环执行了,事件处理程序,可能是多个方法,进行多项操作 ;
                    }
                }
            },
  • 参数: 一个至少包含 type 属性的对象
  • ( 3 ):removeHandler( )方法 ;
  • 过程解析 :
  1.  addHandler()的辅助 ;
  2. 搜索事件处理程序的数组找到要删除的处理程序的位置 ;
  3. 找到了,则使用 break 操作符退出 for 循环 ;
  4. 然后使用 splice()方法将该项目从数组中删除 ;
  5. removeHandler: function (type, handler) {
                if (this.handlers[type] instanceof Array) {
                    var handlers = this.handlers[type];
                    for (var i = 0, len = handlers.length; i < len; i++) {
                        if (handlers[i] === handler) {
                            break;
                        }
                    }
                    handlers.splice(i, 1);
                }
            }
  • 参数: addHandler()的辅助,它们接受的参数一样 ;
  1. 事件的类型 ;
  2. 和事件处理 程序 ;
  • 二、EventTarget 类型的自定义事件的调用  ;
  • function handleMessage(event) { 
            alert("Message received: " + event.message); 
        }
    
        //创建一个新对象 
        var target = new EventTarget(); 
    
        //添加一个事件处理程序 
        target.addHandler("message", handleMessage);  // 添加handleMessage事件方法;
    
        //触发事件 
        target.fire({ type: "message", message: "Hello world!"});   // { type: "message", message: "Hello world!"} 就是传递给事件处理程序 handleMessage(event)的参数 ;
    //删除事件处理程序 
        target.removeHandler("message", handleMessage); 
    
        //再次,应没有处理程序 
        target.fire({ type: "message", message: "Hello world!"}); 

     

  • 三、继承 EventTarget 并获得上述行为 ;

  • 示例:
  • function Person(name, age) {
            EventTarget.call(this);
            this.name = name; this.age = age;
        }
    
        inheritPrototype(Person, EventTarget); // 寄生组合继承
    
        Person.prototype.say = function (message) {
            this.fire({ type: "message", message: message });
        };
  • 备注:
  1. Person 类型使用了寄生组合继承(参见第 6 章)方法来继承 EventTarget ;
  2.  调用了 say() 方法,便触发了事件,它包含了消息的细节 ;
  3. 某种类型的另外的方法中调用 fire()方法是很常见的, 同时它通常不是公开调用的 ;
  • 使用:

   // 定义:Person function Person(name, age) { EventTarget.call(this); this.name = name; this.age = age; } inheritPrototype(Person, EventTarget); // 寄生组合继承 Person.prototype.say = function (message) { this.fire({ type: "message", message: message }); };

 

// 使用 function handleMessage(event) { alert(event.target.name + " says: " + event.message); } var person = new Person("Nicholas", 29); //创建新 person person.addHandler("message", handleMessage); //添加一个事件处理程序 person.say("Hi there."); //在该对象上调用 1 个方法,它触发消息事件 // 不是公开调用 ,调用了一个别的方法触发;

 

为什么使用 :

  1. 代码中存在多个部分在特定时刻相互交互的情况下 ;
  2. 如果每个 对象都有对其他所有对象的引用,那么整个代码就会紧密耦合 ;
  3. 同时维护也变得很困难,因为对某个对 象的修改也会影响到其他对象 ;

使用效果:

  1. 自定义事件有助于解耦相关对象 ;
  2. 保持功能的隔绝 ;
  3. 很多情况中, 触发事件的代码和监听事件的代码是完全分离的 ;
posted @ 2020-09-16 16:15  Amber丶Li  阅读(112)  评论(0)    收藏  举报