- 论坛徽章:
- 0
|
dojo类机制实现原理分析 (二).............
Js代码- 1.function chainedConstructor(bases, ctorSpecial){
- 2. return function(){
- 3. //在此之前有一些准备工作,不详述了
- 4. //找到所有的父类,分别调用其构造方法
- 5. for(i = l - 1; i >= 0; --i){
- 6. f = bases[i];
- 7. m = f._meta;
- 8. f = m ? m.ctor : f;//得到父类的构造方法
- 9. if(f){
- 10. //通过apply调用父类的方法
- 11. f.apply(this, preArgs ? preArgs[i] : a);
- 12. }
- 13. }
- 14. // 请注意在构造方法执行完毕后,会执行名为postscript的方法,而这个方法是//dojo的dijit组件实现的关键生命周期方法
- 15. f = this.postscript;
- 16. if(f){
- 17. f.apply(this, args);
- 18. }
- 19. };
- 20. }
- function chainedConstructor(bases, ctorSpecial){
- return function(){
- //在此之前有一些准备工作,不详述了
- //找到所有的父类,分别调用其构造方法
- for(i = l - 1; i >= 0; --i){
- f = bases[i];
- m = f._meta;
- f = m ? m.ctor : f;//得到父类的构造方法
- if(f){
- //通过apply调用父类的方法
- f.apply(this, preArgs ? preArgs[i] : a);
- }
- }
- // 请注意在构造方法执行完毕后,会执行名为postscript的方法,而这个方法是//dojo的dijit组件实现的关键生命周期方法
- f = this.postscript;
- if(f){
- f.apply(this, args);
- }
- };
- }
复制代码 4. 调用父类方法的实现
在声明dojo类的时候,如果想调用父类的方法一般都是通过使用inherited方法来实现,但从1.4版本开始,dojo支持链式调用所有父类的方法,并引入了一些AOP的概念。我们将会分别介绍这两种方式。
1) 通过inherited方式调用父类方法
在上一篇文章中,我们曾经介绍过,通过在类中使用inherited就可以调用到。这里我们要深入inherited的内部,看一下其实现原理。因为inherited支持调用父类的一般方法和构造方法,两者略有不同,我们关注调用一般方法的过程。
Js代码- 1.function inherited(args, a, f){
- 2. …………
- 3. //在此之前有一些参数的处理
- 4. if(name != cname){
- 5. // 不是构造方法
- 6. if(cache.c !== caller){
- 7. //在此之间的一些代码解决了确定调用者的问题,即确定从什么位置开始找父类
- 8. }
- 9. //按照顺序找父类的同名方法
- 10. base = bases[++pos];
- 11. if(base){
- 12. proto = base.prototype;
- 13. if(base._meta && proto.hasOwnProperty(name)){
- 14. f = proto[name];//找到此方法了
- 15. }else{
- 16. //如果没有找到对应的方法将按照继承链依次往前找
- 17. opf = op[name];
- 18. do{
- 19. proto = base.prototype;
- 20. f = proto[name];
- 21. if(f && (base._meta ? proto.hasOwnProperty(name) : f !== opf)){
- 22. break;
- 23. }
- 24. }while(base = bases[++pos]); // intentional assignment
- 25. }
- 26. }
- 27. f = base && f || op[name];
- 28. }else{
- 29. //此处是处理调用父类的构造方法
- 30. }
- 31. if(f){
- 32. //方法找到后,执行
- 33. return a === true ? f : f.apply(this, a || args);
- 34. }
- 35.}
- function inherited(args, a, f){
- …………
- //在此之前有一些参数的处理
- if(name != cname){
- // 不是构造方法
- if(cache.c !== caller){
- //在此之间的一些代码解决了确定调用者的问题,即确定从什么位置开始找父类
- }
- //按照顺序找父类的同名方法
- base = bases[++pos];
- if(base){
- proto = base.prototype;
- if(base._meta && proto.hasOwnProperty(name)){
- f = proto[name];//找到此方法了
- }else{
- //如果没有找到对应的方法将按照继承链依次往前找
- opf = op[name];
- do{
- proto = base.prototype;
- f = proto[name];
- if(f && (base._meta ? proto.hasOwnProperty(name) : f !== opf)){
- break;
- }
- }while(base = bases[++pos]); // intentional assignment
- }
- }
- f = base && f || op[name];
- }else{
- //此处是处理调用父类的构造方法
- }
- if(f){
- //方法找到后,执行
- return a === true ? f : f.apply(this, a || args);
- }
- }
复制代码 2) 链式调用父类方法
这是从dojo 1.4版本新加入的功能。如果在执行某个方法时,也想按照一定的顺序执行父类的方法,只需在定义类时,在-chains-属性中加以声明即可。
Js代码- 1.dojo.declare("com.levinzhang.Employee", com.levinzhang.Person,{
- 2."-chains-": {
- 3. sayMyself: "before"
- 4. },
- 5.……
- 6.}
- dojo.declare("com.levinzhang.Employee", com.levinzhang.Person,{
- "-chains-": {
- sayMyself: "before"
- },
- ……
- }
复制代码 添加了以上声明后,意味着Employee及其所有的子类,在调用sayMyself方法时,都会先调用本身的同名方法,然后再按照继承链依次调用所有父类的同名方法,我们还可以将值“before”替换为“after”,其执行顺序将会相反。在-chains-属性中声明的方法,在类定义时,会进行特殊处理,正如我们在第一章中看到的那样:
Js代码- 1.if(chains){
- 2. for(name in chains){
- 3. if(proto[name] && typeof chains[name] == "string" && name != cname){
- 4. t = proto[name] = chain(name, bases, chains[name] === "after");
- 5. t.nom = name;
- 6. }
- 7. }
- 8.}
- if(chains){
- for(name in chains){
- if(proto[name] && typeof chains[name] == "string" && name != cname){
- t = proto[name] = chain(name, bases, chains[name] === "after");
- t.nom = name;
- }
- }
- }
复制代码 我们可以看到在-chains-中声明的方法都进行了替换,换成了chain方法的返回值,而这个方法也比较简单,源码如下:
Js代码- 1.function chain(name, bases, reversed){
- 2. return function(){
- 3. var b, m, f, i = 0, step = 1;
- 4. if(reversed){
- 5. //判定顺序,即“after”还是“before”,分别对应于循环的不同起点和方向
- 6. i = bases.length - 1;
- 7. step = -1;
- 8. }
- 9. for(; b = bases[i]; i += step){
- 10. //按照顺序依次查找父类
- 11. m = b._meta;
- 12. //找到父类中同名的方法
- 13. f = (m ? m.hidden : b.prototype)[name];
- 14. if(f){
- 15. //依次执行
- 16. f.apply(this, arguments);
- 17. }
- 18. }
- 19. };
- 20. }
- function chain(name, bases, reversed){
- return function(){
- var b, m, f, i = 0, step = 1;
- if(reversed){
- //判定顺序,即“after”还是“before”,分别对应于循环的不同起点和方向
- i = bases.length - 1;
- step = -1;
- }
- for(; b = bases[i]; i += step){
- //按照顺序依次查找父类
- m = b._meta;
- //找到父类中同名的方法
- f = (m ? m.hidden : b.prototype)[name];
- if(f){
- //依次执行
- f.apply(this, arguments);
- }
- }
- };
- }
复制代码 5. 工具方法和属性如isInstanceOf、declaredClass的实现
除了上面提到的inherited方法以外,dojo在实现类功能的时候,还实现了一些工具方法和属性,这里介绍一个方法isInstanceOf和一个属性declaredClass。从功能上来说isInstanceOf方法用来判断一个对象是否为某个类的实例,而declaredClass属性得到的是某个对象所对应声明类的名字。
Js代码- 1.function isInstanceOf(cls){
- 2. //得到实例对象继承链上的所有类
- 3. var bases = this.constructor._meta.bases;
- 4. //遍历所有的类,看是否与传进来的类相等
- 5. for(var i = 0, l = bases.length; i < l; ++i){
- 6. if(bases[i] === cls){
- 7. return true;
- 8. }
- 9. }
- 10. return this instanceof cls;
- 11.}
- function isInstanceOf(cls){
- //得到实例对象继承链上的所有类
- var bases = this.constructor._meta.bases;
- //遍历所有的类,看是否与传进来的类相等
- for(var i = 0, l = bases.length; i < l; ++i){
- if(bases[i] === cls){
- return true;
- }
- }
- return this instanceof cls;
- }
复制代码 而declaredClass属性的实现比较简单,只是在声明类的原型上添加了一个属性而已,类的实例对象就可以访问这个属性得到其声明类的名字了。这段代码在dojo.declare方法中:
Js代码- 1.if(className){
- 2. proto.declaredClass = className;
- 3. d.setObject(className, ctor);
- 4. }
- if(className){
- proto.declaredClass = className;
- d.setObject(className, ctor);
- }
复制代码 在dojo实现类机制的过程中,有一些内部的方法,是很值得借鉴的如forceNew、safeMixin等,这些方法在实现功能的同时,保证了代码的高效执行,感兴趣的朋友可以进一步的研究。
6. 总结与思考
1) dojo在实现类机制方面支持多继承方式,其它JavaScript类库中很少能做到,而利用JavaScript原生语法实现多继承也较为困难。在这一点上dojo的类机制的功能确实足够强大。但是多继承会增加编码的难度,对开发人员如何组织类也有更高的要求;
2) 链式调用父类方法时,我们可以看到dojo引入了许多AOP的理念,在1.7的版本中,将会有单独的模块提供AOP相关的支持,我们将会持续关注类似的功能;
3) 在dojo的代码中,多处都会出现方法替换,如链式方法调用、事件绑定等,这种设计思想值得我们关注和学习;
4) 使用了许多的内部属性,如_meta、bases等,这些元数据在实现复杂的类机制中起到了至关重要的作用,在进行源码分析的时候,我们可以给予关注,如果要实现类似功能也可以进行借鉴。
探究类库的实现原理是提高自己编码水平的好办法,类似于dojo这样类库的核心代码基本上每一行都有其设计思想在里面(当然也不可以盲目崇拜),每次阅读和探索都会有所发现和心得,当然里面肯定也会有自以为是或谬误之处,在此很乐意和读到这篇文章的朋友们一起研究,欢迎批评指正。 |
|