widget.js中的构造函数代码this.handlers ={};需要去除。要在window.js的Window构造函数中把this.handlers ={};补上。
抽取widget抽象类:widget.js中的构造函数
function Widget(){ this.handlers ={}; }
,和window.js里的
function Window(){ this.handlers ={}; }
这个this.handlers ={};可以去除也只能去除一个【或者两个都保存,保存的话引用的是window自己的handles】。而且应该要去除Widget里的代码
从面向对象的角度来说:对象的主要用途就是本身的属性,方法,其实方法也是xxxObj.xxfun(),也可以理解为一种属性。就是老师说的字典,从这节的代码来看,主要的作用是抽取handles这个obj放置在widget中,【PS:handles里面放的是各个类型的事件数组】。源代码中抽取了widget.js,然后再window.js中$.extends({},new widget.Widget(),{xxx:XXX})中就会把后面两个对象的属性和方法都挂载到了第一个对象{}上了。这时候,其实可以widget.js下的
function Widget(){ this.handlers ={}; }
里的this.handlers ={};注释掉。因为function Window(){}构造函数中已经包含了这个定义了
function Window(){ XXXX..; this.handlers = {}; }
而main.js中实例化对象 var win = new w.Window();new的过程中自然就拥有了Window构造函数中的handles对象。实例win当然可以正确引用到handle。而且,这样引用的handler是自己实例中的handle,搜索树会比较短。效率比较高。如果去除window构造函数的this.handlers = {};这里引用的是window.js中
$.extends({},new widget.Widget(),{xxx:XXX})
new new widget.Widget()产生的handles了。【ps:注意new widget.Widget()之后就产生了一个对象,包含了{key:val}这里就包含了handle:{}这样的key,val了】。
这时候(就是去除Window构造函数的this.handlers = {};不去除widget.js的语句)那么搜索的时候搜索的是Window构造函数原型链上的handler实例。所有的window对象都共享了这个prototype上的handle实例,所以会有bug。假设一个场景(index.html上初始化两个按钮,一个绑定了很多事件,一个不绑定)那么先点击绑定事件的按钮,弹出窗口(这个时候的窗口实例已经绑定了多个事件),然后关闭窗口,再点击不绑定事件的按钮,那么同样会弹出多个事件,因为共享了这个原型对象的handler实例。
简单的说。 Window.prototype= $.extend({},new widget.Widget(), {xxxx}这句代码执行后,如果 Window构造函数中去除this.handlers = {};,那么他找的是Window原型树的handle;如果去除了widget构造函数的this.handlers = {};那么找的是自己的handlers。
-----------------------------------------------------------------------------------------------------------------------
贴上主要代码
main.js,已经将jquery-ui下载到本地了。
/** * Created by Administrator on 2015/7/21. */ require.config({ paths:{ jquery:'jquery-2.1.1', jqueryUI: 'jquery-ui' } }); require(['jquery','window'],function($,w){ $(function(){ $("#btna").on('click',function(){ var win = new w.Window(); win.alert({ width:300, height:150, y:50, content:'hello', title:'title', hasCloseBtn:true, skinClassName:'window_skin_a', textAlertBtn:'hello', dragHandle:'.window_header' }).on("alert",function(){ alert("first alert"); }).on('alert',function(){ alert('second alert'); }).on('alert',function(){ alert("third alsert"); }).on('close',function(){ alert("second close"); }) }); $("#btnb").on('click',function(){ var win = new w.Window(); win.alert({ width:300, height:150, y:50, content:'hello', title:'title', hasCloseBtn:true, skinClassName:'window_skin_a', textAlertBtn:'hello', dragHandle:'.window_header' }); }); }) })
widget.js
/** * Created by Administrator on 15-7-25. */ define(function(){ function Widget(){ // this.handlers = {}; } Widget.prototype = { on:function(type ,handler){ if(typeof this.handlers[type]=='undefined'){ this.handlers[type] =[]; } this.handlers[type].push(handler); return this; }, fire:function(type,data){ if(this.handlers[type] instanceof Array){ var handles= this.handlers[type]; for(var i= 0,len=this.handlers[type].length;i<len;i++){ handles[i](data); } } } } return { Widget:Widget//这里不能加(),不然就跑方法了。 } });
window.js
define(['widget','jquery','jqueryUI'],function(widget,$,$UI){ function Window(){ this.cfg={ width:500, height:300, content:'', handler4AlertBtn:null, handler4CloseBtn:null, hasMask:true, title:'system title', text4AlertBtn:'sure', skinClassName:'', hasCloseBtn:false, isDraggable:true, dragHandle:null }; this.handlers = {}; }; Window.prototype= $.extend({},new widget.Widget(), { alert:function(cfg){ var CFG = $.extend(this.cfg,cfg); var that = this; var boundingBox = $('<div class="window_boundingBox">' + '<div class="window_header">'+CFG.title+'</div>' + '<div class="window_body">'+CFG.content+'</div>' + '<div class="window_footer"><input type="button" class="window_alertBtn" value="'+CFG.text4AlertBtn+'"/></div>' + '</div>'); var mask = null; if(CFG.hasMask){ mask = $('<div class="window_mask"></div>'); mask.appendTo("body"); } if(CFG.isDraggable){ if(CFG.dragHandle){ boundingBox.draggable({handle:CFG.dragHandle}); }else{ boundingBox.draggable(); } } boundingBox.appendTo("body"); if(CFG.skinClassName!==""){ boundingBox.addClass(CFG.skinClassName); } if(CFG.hasCloseBtn){ var closeBtn = $('<span class="window_closeBtn">X</span>'); boundingBox.find(".window_header").append(closeBtn); closeBtn.on("click",function(){ // CFG.handler4CloseBtn&&CFG.handler4CloseBtn(); that.fire("close"); boundingBox.remove(); mask&& mask.remove(); }); } if(CFG.handler4CloseBtn){ this.on('close',CFG.handler4AlertBtn); } if(CFG.handler4AlertBtn){ this.on('alert',CFG.handler4CloseBtn); } var btn = boundingBox.find(".window_footer .window_alertBtn"); btn.on("click",function(){ // CFG.handler4AlertBtn&&CFG.handler4AlertBtn(); that.fire("alert"); boundingBox.remove(); mask&& mask.remove(); }); boundingBox.css({ width:CFG.width+"px", height:CFG.height+"px", left:(CFG.x||(window.innerWidth-CFG.width)/2)+'px', top:(CFG.y||(window.innerHeight-CFG.height)/2)+'px' }); return this; }, prompt:function(){ console.log("prompt"); }, confirm:function(){ console.log("confirm"); } }); return {Window:Window}; })
index.html
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title></title> <link href="css/base.css" type="text/css" rel="stylesheet"/> <link href="css/window.css" type="text/css" rel="stylesheet"/> <script src="js/require.js" data-main="js/main.js"></script> </head> <body> <input type="button" value="alert" id="btna"/> <input type="button" value="alert" id="btnb"/> </body> </html>