前段时间曾经在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如何声明类,后文会对里面的重要细节内容进行介绍):
以上简单介绍了dojo声明类的整体流程,但是一些关键的细节如C3算法、链式调用在后面会继续进行介绍。
2. C3算法的实现
通过以前的文章和上面的分析,我们知道dojo的类声明支持多继承。在处理多继承时,不得不面对的就是继承链如何构造,比较现实的问题是如果多个父类都拥有同名的方法,那么在调用父类方法时,要按照什么规则确定调用哪个父类的呢?在解决这个问题上dojo实现了C3父类线性化的方法,对多个父类进行合理的排序,从而完美解决了这个问题。
为了了解继承链的相关知识,我们看一个简单的例子:
以上的代码中,声明了几个类,通过C3算法得到G的继承顺序应该是这样G->E->C->D->B->A的,只有按照这样的顺序才能保证类定义和依赖是正确的。那我们看一下这个C3算法是如何实现的呢:
通过以上的分析,我们可以看到,这个算法实现起来相当复杂,如果朋友们对其感兴趣,建议按照上文的例子,自己加断点进行调试分析。dojo的作者使用了不到100行的代码实现了这样强大的功能,里面有很多值得借鉴的设计思想。
3. 链式构造器的实现
在第一部分代码分析中我们曾经看到过定义构造函数的代码,如下:
这个方法对于理解dojo类机制很重要。从前一篇文章的介绍中,我们了解到默认情况下,如果dojo声明的类存在继承关系,那么就会自动调用父类的构造方法,且是按照继承链的顺序先调用父类的构造方法,但是从1.4版本开始,dojo提供了手动设置构造方法调用的选项。在以上的代码中涉及到dojo声明类的三个方法,如果该类没有父类,那么调用的就是singleConstructor,如果有父类的话,那么默认调用的是chainedConstructor,如果手动设置了构造方法,那么调用的就是simpleConstructor,要启动这个选项只需在声明该类的时候添加chains的constructor声明即可。
比方说,我们在定义继承自com.levinzhang.Person的com.levinzhang.Employee类时,可以这样做:
添加以上代码后,在构造com.levinzhang.Employee实例时,就不会再调用所有父类的构造方法了,但是此时我们可以使用inherited方法显式的调用父类方法。
限于篇幅,以上的三个方法不全部介绍,只介绍chainedConstructor的核心实现:
4. 调用父类方法的实现
在声明dojo类的时候,如果想调用父类的方法一般都是通过使用inherited方法来实现,但从1.4版本开始,dojo支持链式调用所有父类的方法,并引入了一些AOP的概念。我们将会分别介绍这两种方式。
1) 通过inherited方式调用父类方法
在上一篇文章中,我们曾经介绍过,通过在类中使用inherited就可以调用到。这里我们要深入inherited的内部,看一下其实现原理。因为inherited支持调用父类的一般方法和构造方法,两者略有不同,我们关注调用一般方法的过程。
2) 链式调用父类方法
这是从dojo 1.4版本新加入的功能。如果在执行某个方法时,也想按照一定的顺序执行父类的方法,只需在定义类时,在-chains-属性中加以声明即可。
添加了以上声明后,意味着Employee及其所有的子类,在调用sayMyself方法时,都会先调用本身的同名方法,然后再按照继承链依次调用所有父类的同名方法,我们还可以将值“before”替换为“after”,其执行顺序将会相反。在-chains-属性中声明的方法,在类定义时,会进行特殊处理,正如我们在第一章中看到的那样:
我们可以看到在-chains-中声明的方法都进行了替换,换成了chain方法的返回值,而这个方法也比较简单,源码如下:
5. 工具方法和属性如isInstanceOf、declaredClass的实现
除了上面提到的inherited方法以外,dojo在实现类功能的时候,还实现了一些工具方法和属性,这里介绍一个方法isInstanceOf和一个属性declaredClass。从功能上来说isInstanceOf方法用来判断一个对象是否为某个类的实例,而declaredClass属性得到的是某个对象所对应声明类的名字。
而declaredClass属性的实现比较简单,只是在声明类的原型上添加了一个属性而已,类的实例对象就可以访问这个属性得到其声明类的名字了。这段代码在dojo.declare方法中:
在dojo实现类机制的过程中,有一些内部的方法,是很值得借鉴的如forceNew、safeMixin等,这些方法在实现功能的同时,保证了代码的高效执行,感兴趣的朋友可以进一步的研究。
6. 总结与思考
1) dojo在实现类机制方面支持多继承方式,其它JavaScript类库中很少能做到,而利用JavaScript原生语法实现多继承也较为困难。在这一点上dojo的类机制的功能确实足够强大。但是多继承会增加编码的难度,对开发人员如何组织类也有更高的要求;
2) 链式调用父类方法时,我们可以看到dojo引入了许多AOP的理念,在1.7的版本中,将会有单独的模块提供AOP相关的支持,我们将会持续关注类似的功能;
3) 在dojo的代码中,多处都会出现方法替换,如链式方法调用、事件绑定等,这种设计思想值得我们关注和学习;
4) 使用了许多的内部属性,如_meta、bases等,这些元数据在实现复杂的类机制中起到了至关重要的作用,在进行源码分析的时候,我们可以给予关注,如果要实现类似功能也可以进行借鉴。
探究类库的实现原理是提高自己编码水平的好办法,类似于dojo这样类库的核心代码基本上每一行都有其设计思想在里面(当然也不可以盲目崇拜),每次阅读和探索都会有所发现和心得,当然里面肯定也会有自以为是或谬误之处,在此很乐意和读到这篇文章的朋友们一起研究,欢迎批评指正。
参考资料:
http://docs.dojocampus.org/
http://blog.csdn.net/dojotoolkit/
http://dojotoolkit.org/
作者信息:张卫滨,关注企业级Java开发和RIA技术,个人博客:http://lengyun3566.iteye.com,微博:http://weibo.com/zhangweibin1981
声明:
本文已经首发于InfoQ中文站,版权所有,原文为《dojo类机制实现原理分析》,如需转载,请务必附带本声明,谢谢。
InfoQ中文站是一个面向中高端技术人员的在线独立社区,为Java、.NET、Ruby、SOA、敏捷、架构等领域提供及时而有深度的资讯、高端技术大会如QCon 、线下技术交流活动QClub、免费迷你书下载如《架构师》等。
分享到:
相关推荐
这里我们将深入探讨Dojo的包加载机制,并结合提供的源代码和文档进行分析。 首先,Dojo的包加载机制基于AMD(Asynchronous Module Definition)规范,这允许异步加载和定义模块,确保代码的并行加载和延迟加载,...
通过分析`dojo-release-1.10.0-src`这个源码包,开发者可以深入学习Dojo的内部实现,了解AMD机制、模块化设计以及各种功能的实现细节,这对于提升JavaScript编程技能和理解前端架构有极大的帮助。同时,对于想要贡献...
通过分析和实践这些源代码,开发者不仅可以深入了解Dojo的工作原理,还能掌握实际开发中的技巧和最佳实践。Dojo的源代码是学习和进阶JavaScript及前端开发的宝贵资源,对于想要提升技能和效率的开发者来说,无疑是一...
**DOJO 源代码详解** ...通过深入学习和分析这个压缩包中的源代码和示例,你不仅可以掌握 Dojo 的基本用法,还能理解其设计哲学和内部工作机制,从而在实际项目中更好地利用 Dojo 这个强大的工具。
Dojo 是一个强大的JavaScript工具库,专为构建高性能、可扩展的Web应用程序而设计。这个压缩包中的"dojo-release-1.7.1"代表的是Dojo框架...通过实践这些实例,你可以更好地理解Dojo的工作原理,提升你的Web开发技能。
对于那些希望深入理解dojo Tree工作原理的开发者,阅读源码是一个非常有价值的途径。Dojo Toolkit是开源的,源码可以在GitHub上找到。通过分析源码,可以更好地理解组件内部的事件处理、数据绑定和渲染机制,从而...
3. **DOJO对象和API**:DOJO提供了丰富的API,如DOM操作(`dojo.query()`,`dojo.byId()`),事件处理(`dojo.connect()`),AJAX请求(`dojo.xhrGet()`)等,这些都是在HTML页面中通过JavaScript实现功能的关键。...
通过查看源码,我们可以学习到这些实用工具函数的实现原理,以及如何与浏览器的DOM API进行交互。 再者,Dojo对AJAX有着强大的支持,`dojo/xhr`模块提供了一系列的异步请求方法。在Dojo 1.8.5中,你可以看到如何...
这部分深入探讨了Dojo内部是如何组织其大量源代码的,这对于理解Dojo的工作原理非常重要。 4.3 **加载Dojo** 最后一节介绍了Dojo的加载机制,包括如何按需加载模块以及如何优化加载过程以提高应用程序的性能。 ##...
- **加载机制**:DOJO的加载策略包括动态加载、延迟加载等,以优化页面性能。 - **主题系统**:DOJO的dijit组件库支持多主题,方便UI风格的统一。 - **国际化**:dojo/i18n提供国际化支持,使得应用能够适应不同...
- 分析一个完整的Dojo项目,包括其结构、实现细节和性能优化策略。 - 演示如何利用Dojo创建一个功能完整且用户体验优秀的Web应用。 7. **社区与资源**:提供了关于Dojo社区的信息和资源链接,帮助读者持续学习和...
分析源码可以帮助我们了解Dojo的实现原理,如模块加载机制、事件处理方式、组件构建模式等,这对于提升JavaScript编程技能和理解前端架构设计有极大的帮助。 通过解压并研究“dojo-release-1.0.3-src.zip”,...
3. **Dojo核心模块**:讲解Dojo的核心模块,如dojo/_base/declare,用于实现类的继承和混合;dojo/Stateful,提供属性管理;以及dojo/store,用于数据存储和检索。这些模块是构建复杂应用的重要基石。 4. **Dojo ...
“源码”标签表明这可能涉及到Dojo框架的源代码分析,这对于深入理解Dojo的工作原理、学习如何定制和扩展框架,或者排查问题非常有用。源码学习可以帮助开发者更好地掌握Dojo的核心机制,提升开发效率。 “工具”...
6. **源码分析**:考虑到“源码”标签,书中可能包含部分Dojo源代码的解析,帮助读者理解其内部工作原理,提高开发和调试的效率。 7. **实际应用示例**:为了使理论知识更易于理解,书中很可能包含丰富的实际应用...
总的来说,通过这个 Dojo 示例应用的源码集合,你可以深入理解 Dojo 框架的工作原理,学习如何高效地利用 Dojo 构建现代 Web 应用,并结合 CSS3 技术提升界面表现。在实践中不断探索和研究,你将能更好地掌握 Dojo ...