免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 1584 | 回复: 0
打印 上一主题 下一主题

dojo类机制实现原理分析 (二)............. [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2011-11-26 11:01 |只看该作者 |倒序浏览
dojo类机制实现原理分析 (二).............









Js代码
  1. 1.function chainedConstructor(bases, ctorSpecial){   
  2. 2.        return function(){   
  3. 3.            //在此之前有一些准备工作,不详述了   
  4. 4.             //找到所有的父类,分别调用其构造方法   
  5. 5.            for(i = l - 1; i >= 0; --i){   
  6. 6.                f = bases[i];   
  7. 7.                m = f._meta;   
  8. 8.                f = m ? m.ctor : f;//得到父类的构造方法   
  9. 9.                if(f){   
  10. 10.                      //通过apply调用父类的方法   
  11. 11.                    f.apply(this, preArgs ? preArgs[i] : a);   
  12. 12.                }   
  13. 13.            }   
  14. 14.    // 请注意在构造方法执行完毕后,会执行名为postscript的方法,而这个方法是//dojo的dijit组件实现的关键生命周期方法   
  15. 15.            f = this.postscript;   
  16. 16.            if(f){   
  17. 17.                f.apply(this, args);   
  18. 18.            }   
  19. 19.        };   
  20. 20.    }   
  21. function chainedConstructor(bases, ctorSpecial){
  22.                 return function(){
  23.                         //在此之前有一些准备工作,不详述了
  24.              //找到所有的父类,分别调用其构造方法
  25.                         for(i = l - 1; i >= 0; --i){
  26.                                 f = bases[i];
  27.                                 m = f._meta;
  28.                                 f = m ? m.ctor : f;//得到父类的构造方法
  29.                                 if(f){
  30.                       //通过apply调用父类的方法
  31.                                         f.apply(this, preArgs ? preArgs[i] : a);
  32.                                 }
  33.                         }
  34.         // 请注意在构造方法执行完毕后,会执行名为postscript的方法,而这个方法是//dojo的dijit组件实现的关键生命周期方法
  35.                         f = this.postscript;
  36.                         if(f){
  37.                                 f.apply(this, args);
  38.                         }
  39.                 };
  40.         }  
复制代码
4.  调用父类方法的实现

在声明dojo类的时候,如果想调用父类的方法一般都是通过使用inherited方法来实现,但从1.4版本开始,dojo支持链式调用所有父类的方法,并引入了一些AOP的概念。我们将会分别介绍这两种方式。

     1)  通过inherited方式调用父类方法

在上一篇文章中,我们曾经介绍过,通过在类中使用inherited就可以调用到。这里我们要深入inherited的内部,看一下其实现原理。因为inherited支持调用父类的一般方法和构造方法,两者略有不同,我们关注调用一般方法的过程。

Js代码
  1. 1.function inherited(args, a, f){   
  2. 2.        …………   
  3. 3.         //在此之前有一些参数的处理   
  4. 4.        if(name != cname){   
  5. 5.            // 不是构造方法   
  6. 6.            if(cache.c !== caller){   
  7. 7.                //在此之间的一些代码解决了确定调用者的问题,即确定从什么位置开始找父类   
  8. 8.            }   
  9. 9.            //按照顺序找父类的同名方法   
  10. 10.            base = bases[++pos];   
  11. 11.            if(base){   
  12. 12.                proto = base.prototype;   
  13. 13.                if(base._meta && proto.hasOwnProperty(name)){   
  14. 14.                    f = proto[name];//找到此方法了   
  15. 15.                }else{   
  16. 16.                     //如果没有找到对应的方法将按照继承链依次往前找   
  17. 17.                    opf = op[name];   
  18. 18.                    do{   
  19. 19.                        proto = base.prototype;   
  20. 20.                        f = proto[name];   
  21. 21.                        if(f && (base._meta ? proto.hasOwnProperty(name) : f !== opf)){   
  22. 22.                            break;   
  23. 23.                        }   
  24. 24.                    }while(base = bases[++pos]); // intentional assignment   
  25. 25.                }   
  26. 26.            }   
  27. 27.            f = base && f || op[name];   
  28. 28.        }else{   
  29. 29.        //此处是处理调用父类的构造方法   
  30. 30.        }   
  31. 31.        if(f){   
  32. 32.             //方法找到后,执行   
  33. 33.            return a === true ? f : f.apply(this, a || args);   
  34. 34.        }   
  35. 35.}   
  36. function inherited(args, a, f){
  37.                 …………
  38.          //在此之前有一些参数的处理
  39.                 if(name != cname){
  40.                         // 不是构造方法
  41.                         if(cache.c !== caller){
  42.                                 //在此之间的一些代码解决了确定调用者的问题,即确定从什么位置开始找父类
  43.                         }
  44.                         //按照顺序找父类的同名方法
  45.                         base = bases[++pos];
  46.                         if(base){
  47.                                 proto = base.prototype;
  48.                                 if(base._meta && proto.hasOwnProperty(name)){
  49.                                         f = proto[name];//找到此方法了
  50.                                 }else{
  51.                      //如果没有找到对应的方法将按照继承链依次往前找
  52.                                         opf = op[name];
  53.                                         do{
  54.                                                 proto = base.prototype;
  55.                                                 f = proto[name];
  56.                                                 if(f && (base._meta ? proto.hasOwnProperty(name) : f !== opf)){
  57.                                                         break;
  58.                                                 }
  59.                                         }while(base = bases[++pos]); // intentional assignment
  60.                                 }
  61.                         }
  62.                         f = base && f || op[name];
  63.                 }else{
  64.                 //此处是处理调用父类的构造方法
  65.                 }
  66.                 if(f){
  67.              //方法找到后,执行
  68.                         return a === true ? f : f.apply(this, a || args);
  69.                 }
  70. }
复制代码
2)  链式调用父类方法

这是从dojo 1.4版本新加入的功能。如果在执行某个方法时,也想按照一定的顺序执行父类的方法,只需在定义类时,在-chains-属性中加以声明即可。



Js代码
  1. 1.dojo.declare("com.levinzhang.Employee", com.levinzhang.Person,{   
  2. 2."-chains-": {   
  3. 3.     sayMyself:    "before"  
  4. 4.    },   
  5. 5.……   
  6. 6.}  
  7. dojo.declare("com.levinzhang.Employee", com.levinzhang.Person,{
  8. "-chains-": {
  9.      sayMyself:    "before"
  10.         },
  11. ……
  12. }
复制代码
添加了以上声明后,意味着Employee及其所有的子类,在调用sayMyself方法时,都会先调用本身的同名方法,然后再按照继承链依次调用所有父类的同名方法,我们还可以将值“before”替换为“after”,其执行顺序将会相反。在-chains-属性中声明的方法,在类定义时,会进行特殊处理,正如我们在第一章中看到的那样:



Js代码
  1. 1.if(chains){   
  2. 2.    for(name in chains){   
  3. 3.        if(proto[name] && typeof chains[name] == "string" && name != cname){   
  4. 4.            t = proto[name] = chain(name, bases, chains[name] === "after");   
  5. 5.            t.nom = name;   
  6. 6.        }   
  7. 7.    }   
  8. 8.}   
  9.                 if(chains){
  10.                         for(name in chains){
  11.                                 if(proto[name] && typeof chains[name] == "string" && name != cname){
  12.                                         t = proto[name] = chain(name, bases, chains[name] === "after");
  13.                                         t.nom = name;
  14.                                 }
  15.                         }
  16.                 }
复制代码
我们可以看到在-chains-中声明的方法都进行了替换,换成了chain方法的返回值,而这个方法也比较简单,源码如下:

Js代码
  1. 1.function chain(name, bases, reversed){   
  2. 2.        return function(){   
  3. 3.            var b, m, f, i = 0, step = 1;   
  4. 4.            if(reversed){   
  5. 5.                  //判定顺序,即“after”还是“before”,分别对应于循环的不同起点和方向   
  6. 6.                i = bases.length - 1;   
  7. 7.                step = -1;   
  8. 8.            }   
  9. 9.            for(; b = bases[i]; i += step){   
  10. 10.                //按照顺序依次查找父类   
  11. 11.                m = b._meta;   
  12. 12.                  //找到父类中同名的方法   
  13. 13.                f = (m ? m.hidden : b.prototype)[name];   
  14. 14.                if(f){   
  15. 15.                     //依次执行   
  16. 16.                    f.apply(this, arguments);   
  17. 17.                }   
  18. 18.            }   
  19. 19.        };   
  20. 20.    }   
  21. function chain(name, bases, reversed){
  22.                 return function(){
  23.                         var b, m, f, i = 0, step = 1;
  24.                         if(reversed){
  25.                   //判定顺序,即“after”还是“before”,分别对应于循环的不同起点和方向
  26.                                 i = bases.length - 1;
  27.                                 step = -1;
  28.                         }
  29.                         for(; b = bases[i]; i += step){
  30.                 //按照顺序依次查找父类
  31.                                 m = b._meta;
  32.                   //找到父类中同名的方法
  33.                                 f = (m ? m.hidden : b.prototype)[name];
  34.                                 if(f){
  35.                      //依次执行
  36.                                         f.apply(this, arguments);
  37.                                 }
  38.                         }
  39.                 };
  40.         }
复制代码
5.  工具方法和属性如isInstanceOf、declaredClass的实现

除了上面提到的inherited方法以外,dojo在实现类功能的时候,还实现了一些工具方法和属性,这里介绍一个方法isInstanceOf和一个属性declaredClass。从功能上来说isInstanceOf方法用来判断一个对象是否为某个类的实例,而declaredClass属性得到的是某个对象所对应声明类的名字。



Js代码
  1. 1.function isInstanceOf(cls){   
  2. 2.       //得到实例对象继承链上的所有类   
  3. 3.    var bases = this.constructor._meta.bases;   
  4. 4.        //遍历所有的类,看是否与传进来的类相等   
  5. 5.    for(var i = 0, l = bases.length; i < l; ++i){   
  6. 6.        if(bases[i] === cls){   
  7. 7.            return true;   
  8. 8.        }   
  9. 9.    }   
  10. 10.    return this instanceof cls;   
  11. 11.}  
  12.         function isInstanceOf(cls){
  13.         //得到实例对象继承链上的所有类
  14.                 var bases = this.constructor._meta.bases;
  15.          //遍历所有的类,看是否与传进来的类相等
  16.                 for(var i = 0, l = bases.length; i < l; ++i){
  17.                         if(bases[i] === cls){
  18.                                 return true;
  19.                         }
  20.                 }
  21.                 return this instanceof cls;
  22.         }
复制代码
而declaredClass属性的实现比较简单,只是在声明类的原型上添加了一个属性而已,类的实例对象就可以访问这个属性得到其声明类的名字了。这段代码在dojo.declare方法中:



Js代码
  1. 1.if(className){   
  2. 2.            proto.declaredClass = className;   
  3. 3.            d.setObject(className, ctor);   
  4. 4.        }  
  5. if(className){
  6.                         proto.declaredClass = className;
  7.                         d.setObject(className, ctor);
  8.                 }
复制代码
在dojo实现类机制的过程中,有一些内部的方法,是很值得借鉴的如forceNew、safeMixin等,这些方法在实现功能的同时,保证了代码的高效执行,感兴趣的朋友可以进一步的研究。

6.  总结与思考

1)  dojo在实现类机制方面支持多继承方式,其它JavaScript类库中很少能做到,而利用JavaScript原生语法实现多继承也较为困难。在这一点上dojo的类机制的功能确实足够强大。但是多继承会增加编码的难度,对开发人员如何组织类也有更高的要求;

2)  链式调用父类方法时,我们可以看到dojo引入了许多AOP的理念,在1.7的版本中,将会有单独的模块提供AOP相关的支持,我们将会持续关注类似的功能;

3)  在dojo的代码中,多处都会出现方法替换,如链式方法调用、事件绑定等,这种设计思想值得我们关注和学习;

4)  使用了许多的内部属性,如_meta、bases等,这些元数据在实现复杂的类机制中起到了至关重要的作用,在进行源码分析的时候,我们可以给予关注,如果要实现类似功能也可以进行借鉴。



         探究类库的实现原理是提高自己编码水平的好办法,类似于dojo这样类库的核心代码基本上每一行都有其设计思想在里面(当然也不可以盲目崇拜),每次阅读和探索都会有所发现和心得,当然里面肯定也会有自以为是或谬误之处,在此很乐意和读到这篇文章的朋友们一起研究,欢迎批评指正。
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP