原创作者: abruzzi

接上篇:JavaScript内核系列 第8章 面向对象的JavaScript(上)

8.4实例:事件分发器

这一节,我们通过学习一个面向对象的实例来对JavaScript的面向对象进行更深入的理解,这个例子不能太复杂,涉及到的内容也不能仅仅为继承,多态等概念,如果那样,会失去阅读的乐趣,最好是在实例中穿插一些讲解,则可以得到最好的效果。

本节要分析的实例为一个事件分发器(Event Dispatcher),本身来自于一个实际项目,但同时又比较小巧,我对其代码做了部分修改,去掉了一些业务相关的部分。

事件分发器通常是跟UI联系在一起的,UI中有多个组件,它们之间经常需要互相通信,当UI比较复杂,而页面元素的组织又不够清晰的时候,事件的处理会非常麻烦。在本节的例子中,事件分发器为一个对象,UI组件发出事件到事件分发器,也可以注册自己到分发器,当自己关心的事件到达时,进行响应。如果你熟悉设计模式的话,会很快想到观察者模式,例子中的事件分发器正式使用了此模式。

 

 

 

Js代码

var uikit = uikit || {};   uikit.event = uikit.event || {};       uikit.event.EventTypes = {       EVENT_NONE : 0,       EVENT_INDEX_CHANGE : 1,       EVENT_LIST_DATA_READY : 2,       EVENT_GRID_DATA_READY : 3   };  

var uikit = uikit || {};uikit.event = uikit.event || {}; uikit.event.EventTypes = {    EVENT_NONE : 0,    EVENT_INDEX_CHANGE : 1,    EVENT_LIST_DATA_READY : 2,    EVENT_GRID_DATA_READY : 3};


定义一个名称空间uikit,并声明一个静态的常量:EventTypes,此变量定义了目前系统所支持的事件类型。

Js代码

  1. uikit.event.JSEvent = Base.extend({
  2. constructor : function(obj){
  3. this.type = obj.type || uikit.event.EventTypes.EVENT_NONE;
  4. this.object = obj.data || {};
  5. },
  6. getType : function(){
  7. return this.type;
  8. },
  9. getObject : function(){
  10. return this.object;
  11. }
  12. });
uikit.event.JSEvent = Base.extend({    constructor : function(obj){       this.type = obj.type || uikit.event.EventTypes.EVENT_NONE;       this.object = obj.data || {};    },       getType : function(){       return this.type;    },       getObject : function(){       return this.object;    }});

定义事件类,事件包括类型和事件中包含的数据,通常为事件发生的点上的一些信息,比如点击一个表格的某个单元格,可能需要将该单元格所在的行号和列号包装进事件的数据。

Js代码

  1. uikit.event.JSEventListener = Base.extend({
  2. constructor : function(listener){
  3. this.sense = listener.sense;
  4. this.handle = listener.handle || function(event){};
  5. },
  6. getSense : function(){
  7. return this.sense;
  8. }
  9. });
uikit.event.JSEventListener = Base.extend({    constructor : function(listener){       this.sense = listener.sense;       this.handle = listener.handle || function(event){};    },       getSense : function(){       return this.sense;    }});

定义事件监听器类,事件监听器包含两个属性,及监听器所关心的事件类型sense和当该类型的事件发生后要做的动作handle。

Js代码

  1. uikit.event.JSEventDispatcher = function(){
  2. if(uikit.event.JSEventDispatcher.singlton){
  3. return uikit.event.JSEventDispatcher.singlton;
  4. }
  5. this.listeners = {};
  6. uikit.event.JSEventDispatcher.singlton = this;
  7. this.post = function(event){
  8. var handlers = this.listeners[event.getType()];
  9. for(var index in handlers){
  10. if(handlers[index].handle && typeof handlers[index].handle == "function")
  11. handlers[index].handle(event);
  12. }
  13. };
  14. this.addEventListener = function(listener){
  15. var item = listener.getSense();
  16. var listeners = this.listeners[item];
  17. if(listeners){
  18. this.listeners[item].push(listener);
  19. }else{
  20. var hList = new Array();
  21. hList.push(listener);
  22. this.listeners[item] = hList;
  23. }
  24. };
  25. }
  26. uikit.event.JSEventDispatcher.getInstance = function(){
  27. return new uikit.event.JSEventDispatcher();
  28. };
uikit.event.JSEventDispatcher = function(){    if(uikit.event.JSEventDispatcher.singlton){       return uikit.event.JSEventDispatcher.singlton;    }     this.listeners = {};     uikit.event.JSEventDispatcher.singlton = this;     this.post = function(event){       var handlers = this.listeners[event.getType()];       for(var index in handlers){           if(handlers[index].handle && typeof handlers[index].handle == "function")           handlers[index].handle(event);       }    };     this.addEventListener = function(listener){       var item = listener.getSense();       var listeners = this.listeners[item];       if(listeners){           this.listeners[item].push(listener);       }else{           var hList = new Array();           hList.push(listener);           this.listeners[item] = hList;         }    };} uikit.event.JSEventDispatcher.getInstance = function(){    return new uikit.event.JSEventDispatcher();  };

这里定义了一个单例的事件分发器,同一个系统中的任何组件都可以向此实例注册自己,或者发送事件到此实例。事件分发器事实上需要为何这样一个数据结构:

 

 

Js代码

var listeners = {       eventType.foo : [          {sense : "eventType.foo", handle : function(){doSomething();}}          {sense : "eventType.foo", handle : function(){doSomething();}}          {sense : "eventType.foo", handle : function(){doSomething();}}       ],       eventType.bar : [          {sense : "eventType.bar", handle : function(){doSomething();}}          {sense : "eventType.bar", handle : function(){doSomething();}}          {sense : "eventType.bar", handle : function(){doSomething();}}       ],..   };  

var listeners = {    eventType.foo : [       {sense : "eventType.foo", handle : function(){doSomething();}}       {sense : "eventType.foo", handle : function(){doSomething();}}       {sense : "eventType.foo", handle : function(){doSomething();}}    ],    eventType.bar : [       {sense : "eventType.bar", handle : function(){doSomething();}}       {sense : "eventType.bar", handle : function(){doSomething();}}       {sense : "eventType.bar", handle : function(){doSomething();}}    ],..};


当事件发生之后,分发器会找到该事件处理器的数组,然后依次调用监听器的handle方法进行相应。好了,到此为止,我们已经有了事件分发器的基本框架了,下来,我们开始实现我们的组件(Component)。

组件要通信,则需要加入事件支持,因此可以抽取出一个类:

Js代码

  1. uikit.component = uikit.component || {};
  2. uikit.component.EventSupport = Base.extend({
  3. constructor : function(){
  4. },
  5. raiseEvent : function(eventdef){
  6. var e = new uikit.event.JSEvent(eventdef);
  7. uikit.event.JSEventDispatcher.getInstance().post(e);
  8. },
  9. addActionListener : function(listenerdef){
  10. var l = new uikit.event.JSEventListener(listenerdef);
  11. uikit.event.JSEventDispatcher.getInstance().addEventListener(l);
  12. }
  13. });
uikit.component = uikit.component || {}; uikit.component.EventSupport = Base.extend({  constructor : function(){     },   raiseEvent : function(eventdef){       var e = new uikit.event.JSEvent(eventdef);       uikit.event.JSEventDispatcher.getInstance().post(e);      },   addActionListener : function(listenerdef){       var l = new uikit.event.JSEventListener(listenerdef);       uikit.event.JSEventDispatcher.getInstance().addEventListener(l);  }});

继承了这个类的类具有事件支持的能力,可以raise事件,也可以注册监听器,这个EventSupport仅仅做了一个代理,将实际的工作代理到事件分发器上。

Js代码

  1. uikit.component.ComponentBase = uikit.component.EventSupport.extend({
  2. constructor: function(canvas) {
  3. this.canvas = canvas;
  4. },
  5. render : function(datamodel){}
  6. });
uikit.component.ComponentBase = uikit.component.EventSupport.extend({  constructor: function(canvas) {       this.canvas = canvas;  },   render : function(datamodel){}});

定义所有的组件的基类,一般而言,组件需要有一个画布(canvas)的属性,而且组件需要有展现自己的能力,因此需要实现render方法来画出自己来。

我们来看一个继承了ComponentBase的类JSList:

Js代码

  1. uikit.component.JSList = uikit.component.ComponentBase.extend({
  2. constructor : function(canvas, datamodel){
  3. this.base(canvas);
  4. this.render(datamodel);
  5. },
  6. render : function(datamodel){
  7. var jqo = $(this.canvas);
  8. var text = "";
  9. for(var p in datamodel.items){
  10. text += datamodel.items[p] + ";";
  11. }
  12. var item = $("<div></div>").addClass("component");
  13. item.text(text);
  14. item.click(function(){
  15. jqo.find("div.selected").removeClass("selected");
  16. $(this).addClass("selected");
  17. var idx = jqo.find("div").index($(".selected")[0]);
  18. var c = new uikit.component.ComponentBase(null);
  19. c.raiseEvent({
  20. type : uikit.event.EventTypes.EVENT_INDEX_CHANGE,
  21. data : {index : idx}
  22. });
  23. });
  24. jqo.append(item);
  25. },
  26. update : function(event){
  27. var jqo = $(this.canvas);
  28. jqo.empty();
  29. var dm = event.getObject().items;
  30. for(var i = 0; i < dm.length();i++){
  31. var entity = dm.get(i).item;
  32. jqo.append(this.createItem({items : entity}));
  33. }
  34. },
  35. createItem : function(datamodel){
  36. var jqo = $(this.canvas);
  37. var text = datamodel.items;
  38. var item = $("<div></div>").addClass("component");
  39. item.text(text);
  40. item.click(function(){
  41. jqo.find("div.selected").removeClass("selected");
  42. $(this).addClass("selected");
  43. var idx = jqo.find("div").index($(".selected")[0]);
  44. var c = new uikit.component.ComponentBase(null);
  45. c.raiseEvent({
  46. type : uikit.event.EventTypes.EVENT_INDEX_CHANGE,
  47. data : {index : idx}
  48. });
  49. });
  50. return item;
  51. },
  52. getSelectedItemIndex : function(){
  53. var jqo = $(this.canvas);
  54. var index = jqo.find("div").index($(".selected")[0]);
  55. return index;
  56. }
  57. });
uikit.component.JSList = uikit.component.ComponentBase.extend({    constructor : function(canvas, datamodel){       this.base(canvas);       this.render(datamodel);    },       render : function(datamodel){       var jqo = $(this.canvas);       var text = "";       for(var p in datamodel.items){           text += datamodel.items[p] + ";";       }       var item = $("<div></div>").addClass("component");       item.text(text);       item.click(function(){           jqo.find("div.selected").removeClass("selected");           $(this).addClass("selected");                     var idx = jqo.find("div").index($(".selected")[0]);           var c = new uikit.component.ComponentBase(null);           c.raiseEvent({              type : uikit.event.EventTypes.EVENT_INDEX_CHANGE,              data : {index : idx}           });       });             jqo.append(item);    },       update : function(event){       var jqo = $(this.canvas);       jqo.empty();       var dm = event.getObject().items;        for(var i = 0; i < dm.length();i++){           var entity = dm.get(i).item;           jqo.append(this.createItem({items : entity}));       }    },       createItem : function(datamodel){       var jqo = $(this.canvas);       var text = datamodel.items;        var item = $("<div></div>").addClass("component");       item.text(text);       item.click(function(){           jqo.find("div.selected").removeClass("selected");           $(this).addClass("selected");                     var idx = jqo.find("div").index($(".selected")[0]);           var c = new uikit.component.ComponentBase(null);           c.raiseEvent({              type : uikit.event.EventTypes.EVENT_INDEX_CHANGE,              data : {index : idx}           });       });             return item;    },       getSelectedItemIndex : function(){       var jqo = $(this.canvas);       var index = jqo.find("div").index($(".selected")[0]);       return index;    }});

首先,我们的画布其实是一个共jQuery选择的选择器,选择到这个画布之后,通过jQuery则可以比较容易的在画布上绘制组件。

在我们的实现中,数据与视图是分离的,我们通过定义这样的数据结构:

Js代码

  1. {items : ["China", "Canada", "U.S.A", "U.K", "Uruguay"]};
{items : ["China", "Canada", "U.S.A", "U.K", "Uruguay"]};

则可以render出如下图所示的List:

好,既然组件模型已经有了,事件分发器的框架也有了,相信你已经迫不及待的想要看看这些代码可以干点什么了吧,再耐心一下,我们还要写一点代码:

Js代码

  1. $(document).ready(function(){
  2. var ldmap = new uikit.component.ArrayLike(dataModel);
  3. ldmap.addActionListener({
  4. sense : uikit.event.EventTypes.EVENT_INDEX_CHANGE,
  5. handle : function(event){
  6. var idx = event.getObject().index;
  7. uikit.component.EventGenerator.raiseEvent({
  8. type : uikit.event.EventTypes.EVENT_GRID_DATA_READY,
  9. data : {rows : ldmap.get(idx).grid}
  10. });
  11. }
  12. });
  13. var list = new uikit.component.JSList("div#componentList", []);
  14. var grid = new uikit.component.JSGrid("div#conditionsTable table tbody");
  15. list.addActionListener({
  16. sense :  uikit.event.EventTypes.EVENT_LIST_DATA_READY,
  17. handle : function(event){
  18. list.update(event);
  19. }
  20. });
  21. grid.addActionListener({
  22. sense : uikit.event.EventTypes.EVENT_GRID_DATA_READY,
  23. handle : function(event){
  24. grid.update(event);
  25. }
  26. });
  27. uikit.component.EventGenerator.raiseEvent({
  28. type : uikit.event.EventTypes.EVENT_LIST_DATA_READY,
  29. data : {items : ldmap}
  30. });
  31. var colorPanel = new uikit.component.Panel("div#colorPanel");
  32. colorPanel.addActionListener({
  33. sense : uikit.event.EventTypes.EVENT_INDEX_CHANGE,
  34. handle : function(event){
  35. var idx = parseInt(10*Math.random())
  36. colorPanel.update(idx);
  37. }
  38. });
  39. });
$(document).ready(function(){    var ldmap = new uikit.component.ArrayLike(dataModel);       ldmap.addActionListener({       sense : uikit.event.EventTypes.EVENT_INDEX_CHANGE,       handle : function(event){           var idx = event.getObject().index;           uikit.component.EventGenerator.raiseEvent({              type : uikit.event.EventTypes.EVENT_GRID_DATA_READY,              data : {rows : ldmap.get(idx).grid}           });       }    });       var list = new uikit.component.JSList("div#componentList", []);    var grid = new uikit.component.JSGrid("div#conditionsTable table tbody");       list.addActionListener({        sense :  uikit.event.EventTypes.EVENT_LIST_DATA_READY,        handle : function(event){            list.update(event);        }    });     grid.addActionListener({       sense : uikit.event.EventTypes.EVENT_GRID_DATA_READY,       handle : function(event){           grid.update(event);       }    });     uikit.component.EventGenerator.raiseEvent({       type : uikit.event.EventTypes.EVENT_LIST_DATA_READY,       data : {items : ldmap}    });     var colorPanel = new uikit.component.Panel("div#colorPanel");    colorPanel.addActionListener({       sense : uikit.event.EventTypes.EVENT_INDEX_CHANGE,       handle : function(event){           var idx = parseInt(10*Math.random())           colorPanel.update(idx);       }    });});

使用jQuery,我们在文档加载完毕之后,新建了两个对象List和Grid,通过点击List上的条目,如果这些条目在List的模型上索引发生变化,则会发出EVENT_INDEX_CHAGE事件,接收到这个事件的组件或者DataModel会做出相应的响应。在本例中,ldmap在接收到EVENT_INDEX_CHANGE事件后,会组织数据,并发出EVENT_GRID_DATA_READY事件,而Grid接收到这个事件后,根据事件对象上绑定的数据模型来更新自己的UI。

上例中的类继承关系如下图:

图 事件分发器类层次

应该注意的是,在绑定完监听器之后,我们手动的触发了EVENT_LIST_DATA_READY事件,来通知List可以绘制自身了:

Js代码

  1. uikit.component.EventGenerator.raiseEvent({
  2. type : uikit.event.EventTypes.EVENT_LIST_DATA_READY,
  3. data : {items : ldmap}
  4. });
    uikit.component.EventGenerator.raiseEvent({       type : uikit.event.EventTypes.EVENT_LIST_DATA_READY,       data : {items : ldmap}    });

在实际的应用中,这个事件可能是用户在页面上点击一个按钮,或者一个Ajax请求的返回,等等,一旦事件监听器注册完毕,程序就已经就绪,等待异步事件并响应。

点击List中的元素China,Grid中的数据发生变化

点击Canada,Grid中的数据同样发生相应的变化:

由于List和Grid的数据是关联在一起的,他们的数据结构具有下列的结构:

 

 

Js代码

var dataModel = [{       item: "China",       grid: [           [{               dname: "Beijing",               type: "string"           },           {               dname: "ProductA",               type: "string"           },           {               dname: 1000,               type: "number"           }],           [{               dname: "ShangHai",               type: "string"           },           {               dname: "ProductB",               type: "string"           },           {               dname: 23451,               type: "number"           }],           [{               dname: "GuangZhou",               type: "string"           },           {               dname: "ProductB",               type: "string"           },           {               dname: 87652,               type: "number"           }]       ]   },...   ];  

var dataModel = [{    item: "China",    grid: [        [{            dname: "Beijing",            type: "string"        },        {            dname: "ProductA",            type: "string"        },        {            dname: 1000,            type: "number"        }],        [{            dname: "ShangHai",            type: "string"        },        {            dname: "ProductB",            type: "string"        },        {            dname: 23451,            type: "number"        }],        [{            dname: "GuangZhou",            type: "string"        },        {            dname: "ProductB",            type: "string"        },        {            dname: 87652,            type: "number"        }]    ]},...];


一个组件可以发出多种事件,同时也可以监听多种事件,所以我们可以为List的下标改变事件注册另一个监听器,监听器为一个简单组件Panel,当接收到这个事件后,该Panel会根据一个随机的颜色来重置自身的背景色(注意在List和Grid下面的灰色Panel):

转载于:https://www.cnblogs.com/TDYToBaby/archive/2010/06/12/1757326.html

JavaScript内核系列 第8章 面向对象的JavaScript(下)相关推荐

  1. kmeans python interation flag_Python自学笔记-第六章面向对象编程(下)

    3.魔法方法 Python的对象天生拥有一些神奇的方法,它们总被双下划线所包围,他们是面向对象的 Python 的一切.他们是可以给你的类增加魔力的特殊方法,如果你的对象实现(重载)了这些方法中的某一 ...

  2. 打码进行中-JavaScript高级程序设计-第1章-什么是javaScript

    1.1简短的历史回顾 输入验证 网景 微软 欧洲计算机制造商协会(Ecma) ECMAScript 1.2JavaScript实现 完整的JavaScript实现包含以下几个部分: 核心(ECMASc ...

  3. 【JavaScript 教程】第六章 数组18—push() :将一个或多个元素添加到数组的末尾...

    来源 | https://www.javascripttutorial.net/ 翻译 | 杨小爱 在今天的教程中,我们将学习如何使用 JavaScript Array push()方法将一个或多个元 ...

  4. JavaScript 游戏系列(一): 贪吃蛇

    JavaScript 游戏系列目录 贪吃蛇 文章目录 JavaScript 游戏系列目录 一.实例截图 二.游戏分析 三.HTML 与 CSS 文件 1.入口文件 2.样式文件 四.游戏逻辑的实现 1 ...

  5. 【JavaScript 教程】第六章 数组03— Stack :使用 Array 的push()和pop()方法实现堆栈数据结构...

    英文 | https://www.javascripttutorial.net/ 译文 | 杨小爱 在上节,我们学习了JavaScript Array length属性以及如何正确处理它,错过的小伙伴 ...

  6. 【JavaScript 教程】第六章 数组06— slice() :复制数组元素

    英文 | https://www.javascripttutorial.net/ 译文 | 杨小爱 在上节,我们学习了如何使用 JavaScript Array 的 splice() 方法删除现有元素 ...

  7. JavaScript基础系列之四 面向对象编程

    JavaScript基础系列之四 面向对象编程 面向对象编程 JavaScript的所有数据都可以看成对象,那是不是我们已经在使用面向对象编程了呢? 当然不是.如果我们只使用Number.Array. ...

  8. javascript高级程序设计第3版——第6章 面向对象的程序设计

    第六章--面向对象的程序设计 这一章主要讲述了:面向对象的语言由于没有类/接口情况下工作的几种模式以及面向对象语言的继承: 模式:工厂模式,构造函数模式,原型模式 继承:原型式继承,寄生式继承,以及寄 ...

  9. 深入理解javascript函数系列第二篇——函数参数

    前面的话 javascript函数的参数与大多数其他语言的函数的参数有所不同.函数不介意传递进来多少个参数,也不在乎传进来的参数是什么数据类型,甚至可以不传参数.本文是深入理解javascript函数 ...

最新文章

  1. 异步IO(协程,消息循环队列)
  2. linux apt 命令,Ubuntu系统中apt命令的用法汇总
  3. spring boot原理_SpringBoot-02-原理初探之主启动类
  4. JavaScript实现CountingSort计数排序算法(附完整源码)
  5. C++平衡二叉树(AVL树)
  6. VUE:解决 [Vue warn]: Error in render: “TypeError: item.slice is not a function“ (取部分数据)
  7. matlab学习:人脸识别之LBP (Local Binary Pattern)
  8. java操作Excel实现读写
  9. 多种交换机端口镜像 Port Mirroring 配置
  10. linux 内存清理/释放命令(也可用于openwrt和padavan等系统的路由器)
  11. flink定时器使用问题
  12. amd的服务器cpu型号大全,AMD CPU型号大全
  13. r52500u学计算机,r52500u相当于i几
  14. php获取笔顺矢量,笔顺生成器在线-笔顺生成器php版源码下载-西西软件下载
  15. 高仙机器人四十万能级生产基地项目开工仪式在四川资阳举行
  16. 一个合格的程序员所具备的素质和修养
  17. hp服务器重置bmc,服务器BMC(带外)
  18. C++ Learning 3
  19. Python实现仿射密码
  20. 使用snap安装mosquitto并且进行初步配置

热门文章

  1. C语言 | C语言实现十六进制转八进制
  2. 简述hdfs工作原理_hdfs工作机制和原理 简述hdfs的原理
  3. 智能卡检测控制系统检测m1这么操作_土壤检测实验室仪器设备配置方案
  4. 银行计算机记账比赛,在银行柜台业务技术比赛颁奖仪式上的讲话(一).doc
  5. 近五年计算机网络技术的发展,计算机网络技术的近期发展
  6. PowerBI随笔(4)-关系模型与报表-1
  7. 【Python】刚刚,Python3.10 正式发布了!终于增加了这个功能...
  8. 【NLP】中文情感分类单标签
  9. 【数据分析】Databricks入门:分析COVID-19
  10. 【深度学习】Batch Normalization(BN)超详细解析