dojo.declare在dojo中被广泛使用,理解它将对弄懂dojo的代码有很大帮助。简单的说,dojo.declare提供了一种声明类的方式,通过它可以很方便的扩展其他类:
dojo.declare("com.abc.MyDataProvider", dojo.widget.ComboBoxDataProvider, { name: "My DataProvider", init: function() { ... } } );
这里类名com.abc.MyDataProvider是用字符串来表示的,dojo.widget.ComboBoxDataProvider是被扩展的基类,第三个参数是个object,用来定义该类的属性。 dojo.declare产生的类所具有的属性将是基类的属性和大括号中指定的属性的集合,如果大括号中声明了和基类同名的属性,则基类属性将被覆盖,但也有例外,下面将会说明。生成类的constructor将会调用基类的constructor,然后调用本身的initializer方法,该方法可以来类本身,也可以来自其基类,如果都没有还可以来自dojo.declare中可选的一个参数,否则将是个空函数。
如何使用产生的类呢?可以把该名字作为function类型的变量名来使用,如
dp = new com.abc.MyDataProvider();
可见dojo.declare产生了一个指定名称的变量,引用创建出的class(function) 也可以:
dpClass = dojo.evalObjPath("com.abc.MyDataProvider"); dp = new dpClass();
这种方式比较适合通过类名字符串来使用该类。
如果基类是这么声明的:
dojo.widget.ComboxDataProvdier = function() { this.init = function() { ... } }
而我们希望com.abc.MyDataProvider里声明的init方法覆盖基类的方法,那么上面那个dojo.declare的结果是不对的,这时查看该类的一个实例,你会发现它的init属性是基类的方法。
dojo.declare先会把dojo.widget.ComboBoxDataProvider.prototype上的属性设置到com.abc.MyDataProvider.prototype上,然后再把本身声明的属性加进去,因此com.abc.MyDataProvider.prototype的init属性正是我们期望的init函数,但是当执行该类的constructor方法时,它会调用基类的constructor,因此本来设置好的init函数就被基类的init函数覆盖了,于是override失败。
这里的ComboDataProvider作为一个class,不应该在constructor里赋值init属性,这样会导致每个实例都会有一个init方法的实例,而这是没有必要的。应该用prototype来定义method属性:
com.abc.MyDataProvider.prototype.init = function() { ... }
由此可见,用dojo.declare来override还是有个前提的,那就是要override的method必须用prototype定义,而不是在constructor里。
dojo.declare是可以扩展多个类的,如: dojo.declare("dojo.widget.html.ComboBox", dojo.widget.HtmlWidget,,
{ ... }
); 这时第一个类dojo.widget.HtmlWidget将会被作为super class,于是其prototype上的属性将会进入dojo.widget.html.ComboBox.prototype,然后其他基类prototype上的属性也会被mixin到dojo.widget.html.ComboBox.prototype,最后是类本身声明的属性。当然在constructor只会调用基类的construtor。
这有点类似java的单根继承、多个接口,不过要注意的是:如果dojo.widget.HtmlWidget和dojo.widget.ComboBox同时声明了一个函数属性,那么产生的类将会具有dojo.widget.ComboBox里声明的函数,这时如果dojo.widget.ComboBox因为是一个接口类而把该属性声明为空函数,而你希望继承的是dojo.widget.HtmlWidget上该方法的实现,那么结果就不对了,你得到的类的该方法将是dojo.widget.ComboBox上的声明的空函数。