dojo类机制实现原理分析.doc_第1页
dojo类机制实现原理分析.doc_第2页
dojo类机制实现原理分析.doc_第3页
dojo类机制实现原理分析.doc_第4页
dojo类机制实现原理分析.doc_第5页
已阅读5页,还剩14页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

前段时间曾经在InfoQ中文站上发表文章,介绍了dojo类机制的基本用法。有些朋友在读后希望能够更深入了解这部分的内容,本文将会介绍dojo类机制幕后的知识,其中会涉及到dojo类机制的实现原理并对一些关键方法进行源码分析,当然在此之前希望您能够对JavaScript和dojo的使用有些基本的了解。dojo的类机制支持类声明、继承、调用父类方法等功能。dojo在底层实现上是通过操作原型链来实现其类机制的,而在实现继承时采用类式继承的 方式。值得一提的是,dojo的类机制允许进行多继承(注意,只有父类列表中的第一个作为真正的父类,其它的都是将其属性以mixin的方法加入到子类的 原型链中),为解决多重继承时类方法的顺序问题,dojo用JavaScript实现了Python和其它多继承语言所支持的C3父类线性化算法,以实现 线性的继承关系,想了解更多该算法的知识,可参考这里,我们在后面的分析中将会简单讲解dojo对此算法的实现。1.dojo类声明概览dojo类声明相关的代码位于“/dojo/_base/declare.js”文件中,定义类是通过dojo.declare方法来实现的。关于这个方法的基本用法,已经在dojo类机制简介这篇文章中进行了阐述,现在我们看一下它的实现原理(在这部分的代码分析中,会在整体上介绍dojo如何声明类,后文会对里面的重要细节内容进行介绍):javascript view plaincopyprint?1./此即为dojo.declare方法的定义 2.d.declare = function(className, superclass, props) 3. 4. /前面有格式化参数相关的操作,一般情况下定义类会把三个参数全传进来,分别为 5./类名、父类(可以为null、某个类或多个类组成的数组)和要声明类的属性及方法 6. 7./定义一系列的变量供后面使用 8. var proto, i, t, ctor, name, bases, chains, mixins = 1, parents = superclass; 9. 10. / 处理要声明类的父类 11. if(opts.call(superclass) = object Array) 12. /如果父类参数传过来的是数组,那么这里就是多继承,要用C3算法处理父类的关系 13. /得到的bases为数组,第一个元素能标识真正父类(即superclass参数中的第一个)/在数组中的索引,其余的数组元素是按顺序排好的继承链,后面还会介绍到C3算法 14. bases = c3mro(superclass, className); 15. t = bases0; 16. mixins = bases.length - t; 17. superclass = basesmixins; 18. else 19. /此分支内是对没有父类或单个父类情况的处理,不再详述 20. 21. /以下为构建类的原型属性和方法 22. if(superclass) 23. for(i = mixins - 1; -i) 24. /此处遍历所有需要mixin的类 25. /注意此处,为什么说多个父类的情况下,只有第一个父类是真正的父类呢,因/为在第一次循环的实例化了该父类,并记在了原型链中,而其它需要mixin的/父类在后面处理时会把superclass设为一个空的构造方法,合并父类原型链/后进行实例化 26.proto = forceNew(superclass); 27. if(!i) 28. /此处在完成最后一个父类后跳出循环 29. break; 30. 31. / mix in properties 32. t = basesi;/得到要mixin的一个父类 33. (t._meta ? mixOwn : mix)(proto, totype);/合并原型链 34. / chain in new constructor 35. ctor = new Function;/声明一个新的Function 36. ctor.superclass = superclass; 37. totype = proto;/设置原型链 38./此时将superclass指向了这个新的Function,再次进入这个循环的时候,实例/化的是ctor,而不是mixin的父类 39. superclass = proto.constructor = ctor; 40. 41. else 42. proto = ; 43. 44. /此处将上面得到的方法(及属性)与要声明类本身所拥有的方法(及属性)进行合并 45. safeMixin(proto, props); 46. 47. 48. /此处收集链式调用相关的信息,后面会详述 49. for(i = mixins - 1; i; -i) / intentional assignment 50. t = basesi._meta; 51. if(t & t.chains) 52. chains = mix(chains | , t.chains); 53. 54. 55. if(proto-chains-) 56. chains = mix(chains | , proto-chains-); 57. 58. 59. /此处根据上面收集的链式调用信息和父类信息构建最终的构造方法,后文详述 60. t = !chains | !chains.hasOwnProperty(cname); 61. bases0 = ctor = (chains & chains.constructor = manual) ? simpleConstructor(bases) : 62. (bases.length = 1 ? singleConstructor(props.constructor, t) : chainedConstructor(bases, t); 63. 64. /在这个构造方法中添加了许多的属性,在进行链式调用以及调用父类方法等处会用到 65. ctor._meta = bases: bases, hidden: props, chains: chains, 66. parents: parents, ctor: props.constructor; 67. ctor.superclass = superclass & totype; 68. ctor.extend = extend; 69. totype = proto; 70. proto.constructor = ctor; 71. 72. / 对于dojo.declare方法声明类的实例均有以下的工具方法 73. proto.getInherited = getInherited; 74. proto.inherited = inherited; 75. proto.isInstanceOf = isInstanceOf; 76. 77. / 此处要进行全局注册 78. if(className) 79. proto.declaredClass = className; 80. d.setObject(className, ctor); 81. 82. 83. /对于链式调用父类的那些方法进行处理,实际上进行了重写,后文详述 84. if(chains) 85. for(name in chains) 86. if(protoname & typeof chainsname = string & name != cname) 87. t = protoname = chain(name, bases, chainsname = after); 88. t.nom = name; 89. 90. 91. 92. return ctor;/Function 93.; 以上简单介绍了dojo声明类的整体流程,但是一些关键的细节如C3算法、链式调用在后面会继续进行介绍。2 C3算法的实现通 过以前的文章和上面的分析,我们知道dojo的类声明支持多继承。在处理多继承时,不得不面对的就是继承链如何构造,比较现实的问题是如果多个父类都拥有 同名的方法,那么在调用父类方法时,要按照什么规则确定调用哪个父类的呢?在解决这个问题上dojo实现了C3父类线性化的方法,对多个父类进行合理的排 序,从而完美解决了这个问题。为了了解继承链的相关知识,我们看一个简单的例子:javascript view plaincopyprint?1.dojo.declare(A,null); 2.dojo.declare(B,null); 3.dojo.declare(C,null); 4.dojo.declare(D,A, B); 5.dojo.declare(E,B, C); 6. dojo.declare(F,A, C); 7. dojo.declare(G,D, E); 以上的代码中,声明了几个类,通过C3算法得到G的继承顺序应该是这样G-E-C-D-B-A的,只有按照这样的顺序才能保证类定义和依赖是正确的。那我们看一下这个C3算法是如何实现的呢:javascript view plaincopyprint?1.function c3mro(bases, className) 2. /定义一系列的变量 3. var result = , roots = cls: 0, refs: , nameMap = , clsCount = 1, 4. l = bases.length, i = 0, j, lin, base, top, proto, rec, name, refs; 5. 6. /在这个循环中,构建出了父类各自的依赖关系(即父类可能会依赖其它的类) 7. for(; i = 0; -j) 14. /遍历继承链中的元素,注意,这里的处理是反向的,即从最底层的开始,一直到链的顶端 15. proto = totype; 16. if(!proto.hasOwnProperty(declaredClass) 17. proto.declaredClass = uniqName_ + (counter+); 18. 19. name = proto.declaredClass; 20. / nameMap以map的方式记录了用到的类,不会重复 21. if(!nameMap.hasOwnProperty(name) 22. /每个类都会有这样一个结构,其中refs特别重要,记录了引用了依赖类 23. nameMapname = count: 0, refs: , cls: linj; 24. +clsCount; 25. 26. rec = nameMapname; 27. if(top & top != rec) 28. /满足条件时,意味着当前的类依赖此时top引用的类,即链的前一元素 29. rec.refs.push(top); 30. +top.count; 31. 32. top = rec;/top指向当前的类,开始下一循环 33. 34. +top.count; 35. roots0.refs.push(top);/在一个父类处理完成后就将它放在根的引用中 36. 37./到此为止,我们建立了父类元素的依赖关系,以下要正确处理这些关系 38. while(roots.length) 39.top = roots.pop(); 40./将依赖的类放入结果集中 41. result.push(top.cls); 42. -clsCount; 43. / optimization: follow a single-linked chain 44. while(refs = top.refs, refs.length = 1) 45. /若当前类依赖的是一个父类,那处理这个依赖链 46. top = refs0; 47. if(!top | -top.count) 48. /特别注意此时有一个top.count变量,是用来记录这个类被引用的次数,/如果减一之后,值还大于零,说明后面还有引用,此时不做处理,这也就是/在前面的例子中为什么不会出现G-E-C-B的原因 49. top = 0; 50. break; 51. 52. result.push(top.cls); 53. -clsCount; 54. 55. if(top) 56. /若依赖多个分支,则将依赖的类分别放到roots中,这段代码只有在多继承,/第一次进入时才会执行 57. for(i = 0, l = refs.length; i = 0; -i) 6. f = basesi; 7. m = f._meta; 8. f = m ? m.ctor : f;/得到父类的构造方法 9. if(f) 10. /通过apply调用父类的方法 11. f.apply(this, preArgs ? preArgsi : a); 12. 13. 14. / 请注意在构造方法执行完毕后,会执行名为postscript的方法,而这个方法是/dojo的dijit组件实现的关键生命周期方法 15. f = this.postscript; 16. if(f) 17. f.apply(this, args); 18. 19. ; 20. 4 调用父类方法的实现在声明dojo类的时候,如果想调用父类的方法一般都是通过使用inherited方法来实现,但从1.4版本开始,dojo支持链式调用所有父类的方法,并引入了一些AOP的概念。我们将会分别介绍这两种方式。1) 通过inherited方式调用父类方法在上一篇文章中,我们曾经介绍过,通过在类中使用inherited就可以调用到。这里我们要深入inherited的内部,看一下其实现原理。因为inherited支持调用父类的一般方法和构造方法,两者略有不同,我们关注调用一般方法的过程。javascript view plaincopyprint?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 = totype; 13. if(base._meta & proto.hasOwnProperty(name) 14. f = protoname;/找到此方法了 15. else 16. /如果没有找到对应的方法将按照继承链依次往前找 17. opf = opname; 18. do 19. proto = totype; 20. f = protoname; 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 | opname; 28. else 29. /此处是处理调用父类的构造方法 30. 31. if(f) 32. /方法找到后,执行 33. return a = true ? f : f.apply(this, a | args); 34. 35. 2) 链式调用父类方法这是从dojo 1.4版本新加入的功能。如果在执行某个方法时,也想按照一定的顺序执行父类的方法,只需在定义类时,在-chains-属性中加以声明即可。javascript view plaincopyprint?1.dojo.declare(com.levinzhang.Employee, com.levinzhang.Person, 2.-chains-: 3. sayMyself: before 4. , 5. 6. 添加了以上声明后,意味着Employee及其所有的子类,在调用sayMyself方法时,都会 先调用本身的同名方法,然后再按照继承链依次调用所有父类的同名方法,我们还可以将值“before”替换为“after”,其执行顺序将会相反。在 -chains-属性中声明的方法,在类定义时,会进行特殊处理,正如我们在第一章中看到的那样:javascript view plaincopyprint?1.if(chains) 2. for(name in chains) 3. if(protoname & typeof chainsname = string & name != cname) 4. t = protoname = chain(name, bases, chainsname = after); 5. t.nom = name; 6. 7. 8. 我们可以看到在-chains-中声明的方法都进行了替换,换成了chain方法的返回值,而这个方法也比较简单,源码如下:javascript view plaincopyprint?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 = basesi; i += step) 10. /按照顺序依次查找父类 11. m = b._meta; 12. /找到父类中同名的方法 13. f = (m ? m.hidden : totype)name; 14. if(f) 15. /依次执行 16. f.apply(this, arguments); 17. 18. 19. ; 20. 5工具方法和属性如isInstanceOf、declaredClass的实现除 了上面提到的inherited方法以外,dojo在实现类功能的时候,还实现了一些工具方法和属性,这里介绍一个方法isInstanceOf和一个属 性declaredClass。从功能上来说isInstanceOf方法用来判断一个对象是否为某个类的实例,而declaredClass属性得到的 是某个对象所对应声明类的名字。javascript view plaincopyprint?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(basesi = cls) 7. return true; 8. 9. 10. return this instanceof cls; 11. 而declaredClass属性的实现比较简单,只是在声明类的原型上添加了一个属性而已,类的实例对象就可以访问这个属性得到其声明类的名字了。这段代码在d

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论