分享

创建并增强Dojo类

 jacklopy 2011-04-11
      原作者David Walsh
像所有一流的JavaScript工具包一样,Dojo致力于将类做得尽可能灵活,因为它知道不同的用户可能对一个类及其方法是如何工作的抱有完全不同的看法。幸运的是,Dojo提供了大量的方法使您能够继承或修改类。现在我们就来研究其中的一些方法,它们可以让你随心所欲地摆弄Dojo类。
创建Dojo子类
创建Dojo类或子类的一个典型方法是用dojo.declare。dojo.declare把类注册到你所指定的名字空间中,并且能够继承任意数量的类(由第二个参数传入)。
以下代码展示了创建子类的基本写法:
view plaincopy to clipboardprint?
01.//dojo.declare(’your.name.space’,superClass,{ customProperties });  
02.//或者:  
03.//dojo.declare(’your.name.space’,[superClass1,superClass2,superClass3],{ customProperties });  
04.dojo.provide(‘davidwalsh.Menu’);  
05.dojo.declare(‘davidwalsh.Menu’,dijit.Menu,{  
06.        /* 添加你自定义的属性和方法 */ 
07.        myCustomProperty: true,  
08.        myCustomMethod: function() {  
09.                /* 做一些有趣的事…… */ 
10.        }  
11.        //dijit.Menu的所有属性和方法都被自动继承  
12.}); 
//dojo.declare(’your.name.space’,superClass,{ customProperties });
//或者:
//dojo.declare(’your.name.space’,[superClass1,superClass2,superClass3],{ customProperties });
dojo.provide(‘davidwalsh.Menu’);
dojo.declare(‘davidwalsh.Menu’,dijit.Menu,{
        /* 添加你自定义的属性和方法 */
        myCustomProperty: true,
        myCustomMethod: function() {
                /* 做一些有趣的事…… */
        }
        //dijit.Menu的所有属性和方法都被自动继承
});
以上代码创建的新类davidwalsh.Menu就是一个新的自定义Dojo类,它继承了dijit.Menu类的所有方法和属性,而且还添加了一个新的自定义属性以及一个自定义方法,可以用来做任何事情。既然我们已经知道如何创建子类了,那就让我们来写一个实用一点的davidwalsh.Menu吧:
view plaincopy to clipboardprint?
01.dojo.provide(‘davidwalsh.Menu’);  
02.dojo.declare(‘davidwalsh.Menu’,dijit.Menu,{  
03.        //一个新的选项  
04.        allowSubmenuHover: true,  
05.        //另一个新的选项  
06.        popupDelay: 500,  
07.        //重载dijit.Menu的方法  
08.        onItemHover: function(item) {  
09.                if(this.isActive || this.allowSubmenuHover) {  
10.                        this.focusChild(item);  
11.                        //使用新的设置来触发弹出菜单  
12.                        if(this.focusedChild.popup &&  
13.                          !this.focusedChild.disabled &&  
14.                          !this.hover_timer){  
15.                                this.hover_timer = setTimeout(  
16.                                  dojo.hitch(this, ‘_openPopup’),  
17.                                  this.popupDelay);  
18.                        }  
19.                }  
20.                if(this.focusedChild){  
21.                        this.focusChild(item);  
22.                }  
23.                this._hoveredChild = item;  
24.        }  
25.}); 
dojo.provide(‘davidwalsh.Menu’);
dojo.declare(‘davidwalsh.Menu’,dijit.Menu,{
        //一个新的选项
        allowSubmenuHover: true,
        //另一个新的选项
        popupDelay: 500,
        //重载dijit.Menu的方法
        onItemHover: function(item) {
                if(this.isActive || this.allowSubmenuHover) {
                        this.focusChild(item);
                        //使用新的设置来触发弹出菜单
                        if(this.focusedChild.popup &&
                          !this.focusedChild.disabled &&
                          !this.hover_timer){
                                this.hover_timer = setTimeout(
                                  dojo.hitch(this, ‘_openPopup’),
                                  this.popupDelay);
                        }
                }
                if(this.focusedChild){
                        this.focusChild(item);
                }
                this._hoveredChild = item;
        }
});

davidwalsh.Mene是dijit.Menu类的增强版。它具有两个新的选项,并重载了一个dijit.Menu的方法。其目的在于提供一种在鼠标悬停时而非单击时打开弹出菜单项的菜单。
你可能在想,怎样在子类的方法里调用父类的方法呢?这也很简单:
view plaincopy to clipboardprint?
01.//……一些其他方法  
02.someMethod: function() {  
03.        /* 在这里做一些你想做的事…… */ 
04.        // 调用父类的someMethod方法,从而重用原有的功能。  
05.        var result = this.inherited(arguments);  
06.        /* 在这里做另一些你想做的事…… */ 
07.}  
08.//……另一些其他方法 
//……一些其他方法
someMethod: function() {
        /* 在这里做一些你想做的事…… */
        // 调用父类的someMethod方法,从而重用原有的功能。
        var result = this.inherited(arguments);
        /* 在这里做另一些你想做的事…… */
}
//……另一些其他方法
可见,建立子类易如反掌。但如果你只需要修改一个现存的Dojo类,又该怎么做呢?答案是打补丁!
原型修改(Monkey Patching)
有时,继承一个现有的Dojo类并不是最好的选择(甚至有可能根本无法这样做)。你很可能处于这样一个境地,只能给现有的Dojo类打补丁,这时Monkey Patching就是最理想的选择。所谓Monkey Patching就是一个修改现有对象(在这里指Dojo类)的原型的过程。这一做法的优点有:
•这个类型的所有现存对象都被同时修改了。
•不需要访问Dojo核心文件.
•由于你并没有修改Dojo的核心文件,升级Dojo版本就变得相对容易,因为你不用跟踪你以前的改动。
•你的补丁也将具有更好的可移植性,因为它们不是直接放在Dojo核心文件里。
以下代码展示了Monkey Patching的模式:
view plaincopy to clipboardprint?
01.(function(){  
02.        //保存原有的原型方法  
03.        var oldPrototypeSomeMethod =  
04.                dijit.someDijit.prototype.someMethod;  
05.        //修改原型  
06.        dijit.someDijit.prototype.someMethod = function(){  
07.        /* ……这里是一些新代码…… */ 
08.        // 仅当你希望保留原有功能时,调用刚才你保存的原有原型方法  
09.        oldPrototypeSomeMethod.call(this, arguments);  
10.    };  
11.})(); 
(function(){
        //保存原有的原型方法
        var oldPrototypeSomeMethod =
                dijit.someDijit.prototype.someMethod;
        //修改原型
        dijit.someDijit.prototype.someMethod = function(){
        /* ……这里是一些新代码…… */
        // 仅当你希望保留原有功能时,调用刚才你保存的原有原型方法
        oldPrototypeSomeMethod.call(this, arguments);
    };
})();
现在我们来看一个实际例子。我最近正在使用FilteringSelect控件,我发现如果srcNode(也就是select元素)里的第一个option元素没有value属性(或者value为空字符串),那么这个元素的label就不会显示。这是一个非常奇怪的bug,并且肯定不是一个符合用户预期的行为。我所能做的就是给这个控件类的postMixInProperties方法打上补丁来修复这个问题:
view plaincopy to clipboardprint?
01.(function(){  
02.        var dffsp = dijit.form.FilteringSelect.prototype;  
03.        //保存原有的原型方法  
04.        var oldPMIP = dffsp.postMixInProperties;  
05.        //修改控件原型  
06.        dffsp.postMixInProperties = function(){  
07.          //如果select元素当前没有值,并且其首个选项的value属性是空字符串,就把控件的displayedValue设为初始标签。  
08.          if(!this.store && this.srcNodeRef.value == ”){  
09.                var srcNodeRef = this.srcNodeRef,  
10.                nodes = dojo.query("> option[value='']", srcNodeRef);  
11.                if(nodes.length){  
12.                        this.displayedValue =  
13.                                dojo.trim(nodes[0].innerHTML);  
14.                }  
15.          }  
16.        // 调用原有的方法。我们仍然需要这些原有功能。  
17.        oldPMIP.call(this, arguments);  
18.    };  
19.})(); 
(function(){
        var dffsp = dijit.form.FilteringSelect.prototype;
        //保存原有的原型方法
        var oldPMIP = dffsp.postMixInProperties;
        //修改控件原型
        dffsp.postMixInProperties = function(){
          //如果select元素当前没有值,并且其首个选项的value属性是空字符串,就把控件的displayedValue设为初始标签。
          if(!this.store && this.srcNodeRef.value == ”){
                var srcNodeRef = this.srcNodeRef,
                nodes = dojo.query("> option[value='']", srcNodeRef);
                if(nodes.length){
                        this.displayedValue =
                                dojo.trim(nodes[0].innerHTML);
                }
          }
        // 调用原有的方法。我们仍然需要这些原有功能。
        oldPMIP.call(this, arguments);
    };
})();

这只是使用Monkey Patching的好例子之一。虽然打补丁可能看起来属于不太优雅的技术,但它的确是定制你手里Dojo包的一个必要方法。
扩展Dojo类
Dojo.extend方法允许我们为类的原型添加新方法,从而为该类的所有实例提供这些方法。如果传给dojo.extend的某个方法在类中已经存在同名方法,那它将覆盖这个原有方法。
下面的例子展示了如果和扩展dijit.Menu类,从而使弹出菜单在鼠标悬浮在标签上时显示,而不是单击时。
view plaincopy to clipboardprint?
01.dojo.extend(dijit.Menu,{  
02.        allowSubmenuHover: true, //一个新的设置选项  
03.        popupDelay: 500, //一个新的设置选项  
04.        onItemHover: function() { //重载父类方法  
05.                if(this.isActive || this.allowSubmenuHover) {  
06.                        this.focusChild(item);  
07.                        //使用新选项触发弹出菜单  
08.                        if(this.focusedChild.popup &&  
09.                          !this.focusedChild.disabled &&  
10.                          !this.hover_timer){  
11.                            this.hover_timer = setTimeout(dojo.hitch(  
12.                                  this, ‘_openPopup’), this.popupDelay);  
13.                        }  
14.                }  
15.                if(this.focusedChild){  
16.                        this.focusChild(item);  
17.                }  
18.                this._hoveredChild = item;  
19.        }  
20.}); 
dojo.extend(dijit.Menu,{
        allowSubmenuHover: true, //一个新的设置选项
        popupDelay: 500, //一个新的设置选项
        onItemHover: function() { //重载父类方法
                if(this.isActive || this.allowSubmenuHover) {
                        this.focusChild(item);
                        //使用新选项触发弹出菜单
                        if(this.focusedChild.popup &&
                          !this.focusedChild.disabled &&
                          !this.hover_timer){
                            this.hover_timer = setTimeout(dojo.hitch(
                                  this, ‘_openPopup’), this.popupDelay);
                        }
                }
                if(this.focusedChild){
                        this.focusChild(item);
                }
                this._hoveredChild = item;
        }
});
注意到原有的onItemHover方法并没有被保存下来并在以后使用,而是整个原型都被重写了。因为我们正是要抛弃这个方法原来的功能。现在我们拥有了一个满足我们需求的dijit.Menu类,并且我们的Dojo包中的文件并没有受到干扰。
继承,扩展,还是打补丁?
对于一个类,何时应该扩展何时应该补丁并没有一个硬性的规定。但我的确有几点建议:
•不要修改任何Dojo核心文件,必要时打补丁或做扩展。
•如果你需要访问原有的的原型对象,使用打补丁的方法。
•当希望在多个项目之间重用代码时,使用你自定义的名字空间创建子类。
•如果可移植性是一个很重要的因素,使用扩展类的方法。
尽量扩展!
扩展Dojo类是修复bug、增强Dojo内置类、以及避免重复代码的最佳途径。Dojo里没有任何限制,除了那些你强加给它的!

本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/dojotoolkit/archive/2010/09/19/5893763.aspx

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多