function ClassA(sColor) { @H_502_17@ this.color = sColor; @H_502_17@ this.xxx = xxx; @H_502_17@ ......@H_502_17@ }
function ClassB(sColor,name) { @H_502_17@ this.newMethod = ClassA; @H_502_17@ this.newMethod(sColor); @H_502_17@ delete this.newMethod; @H_502_17@ ......@H_502_17@ }
@H_403_0@或者:(这里ClassA同上)function ClassB(sColor,sName) { @H_502_17@ ClassA.call(this,sColor); //或者apply @H_502_17@ @H_502_17@ this.name = sName; @H_502_17@ this.sayName = function() { @H_502_17@ alert(this.name); @H_502_17@ }; @H_502_17@ }
@H_403_0@2. 原型链式继承://基类
function ClassA() { } @H_502_17@ ClassA.... = .....; @H_502_17@ //子类 @H_502_17@ function ClassB() { } @H_502_17@ //通过修改prototype来达到继承目的@H_502_17@ ClassB.prototype = new ClassA(); @H_502_17@ //定义新属性与方法 @H_502_17@ ClassB.... = .....;
//保证constructor的一致性(修改prototype后其constructor变为ClassA,应该是ClassB)@H_502_17@
ClassB.prototype.constructor = ClassB;
@H_403_0@上述两种方法各有优缺点:对象冒充方式效率低,而且必须使用构造函数方式但是能支持多继承,而prototype方式很灵活但不支持多继承。于是有了如下混合方式:function ClassA(sColor) { } @H_502_17@ ClassA.... = ..... //设置属性@H_502_17@ @H_502_17@ function ClassB() { @H_502_17@ ClassA.call(this,sColor); //对象冒充方式 继承属性 @H_502_17@ this.name = sName; @H_502_17@ } @H_502_17@ ClassB.prototype = new ClassA(); //原型链方式 继承方法 @H_502_17@ ClassB.prototype.sayName = function() { @H_502_17@ alert(this.name); @H_502_17@ }
@H_403_0@下面我们来看看dojo是如何实现继承机制的: @H_403_0@下面是dojo中声明类和使用类的一个简单示例,可以看出,其核心在一个dojo.declare函数上: @H_403_0@ @H_403_0@ @H_403_0@我们来看看dojo.declare函数: @H_403_0@1> 先看第一个参数:className参数,可以见如下相关代码:......
// add name if specified@H_502_17@ if(className){@H_502_17@ proto.declaredClass = className;@H_502_17@ d.setObject(className,ctor); @H_502_17@ }
......
@H_403_0@其中ctor是在该方法内构造的一个函数,其实是该声明类的定义,其本身是一个函数对象,里面有很多成员:包括属性定义,函数定义,关于这个的详细介绍这个后面会涉及到。这里主要通过dojo.setObject将该构造的函数对象赋给名为className的变量,如dijit.WidgetSet. @H_403_0@所以这里相当于这样写: @H_403_0@ctor = function(){....}; @H_403_0@Dijit.WidgetSet = ctor; @H_403_0@另外强调一下,这里的proto就是ctor的prototype. @H_403_0@2> 我们再来看看第三个参数:props,可以见如下代码:(具体可见加粗的字体说明)............@H_502_17@ @H_502_17@ proto = {};
@H_502_17@ //复制一份类的定义对象的内容,见上面Dijit.Calendar的templateString,value,buildRendering等等属性和方法.@H_502_17@ safeMixin(proto,props);
@H_502_17@ @H_502_17@ // 开始构造ctor,即构造类的定义,可以看到里面还有很多dojo自己添加的属性 @H_502_17@ t = !chains || !chains.hasOwnProperty(cname);
//以下三个对象singleConstructor/simpleConstructor/chainedConstructor 都是 是一个函数对象,其返回值也是一个函数对像,这些返回的函数对象内部,都会包含一些默认操作,包括初始化,调用ctor及其基类的ctor等等,具体细节可以参见上述三个函数的定义,可以看出模拟dojo的类的函数在初始化时都做了些什么,很值得研究,为了是篇幅不要太长,这里暂时不做过于细致的深入。 @H_502_17@ bases[0] = ctor = (chains && chains.constructor === "manual") ? simpleConstructor(bases) :@H_502_17@ (bases.length == 1 ? singleConstructor(props.constructor,t) : chainedConstructor(bases,t));@H_502_17@ @H_502_17@ // add Meta information to the constructor
//这里的ctor属性很关键,在上面提到的 singleConstructor/simpleConstructor/chainedConstructor函数里面会调用到,这也是为什么dojo.declare的“类”在初始化时(new操作)会自动调用constructor方法的原因!!! @H_502_17@ ctor._Meta = {bases: bases,hidden: props,chains: chains,@H_502_17@ parents: parents,ctor: props.constructor };@H_502_17@ ctor.superclass = superclass && superclass.prototype;@H_502_17@ ctor.extend = extend;
//这里是比较关键的操作了,将定义的属性全部作为prototype的属性,注意我们这里先不谈继承,而且这里不是在继承,请和前面的javascript继承区分开
ctor.prototype = proto;
//由于prototype被修改,所以需要重新赋值以保持prototype的constructor一致性 @H_502_17@ proto.constructor = ctor;@H_502_17@ @H_502_17@ // 添加dojo里面比较常用的方法,注意:这些方法被直接加到了prototype中! @H_502_17@ proto.getInherited = getInherited;@H_502_17@ proto.inherited = inherited;@H_502_17@ proto.isInstanceOf = isInstanceOf;@H_502_17@ @H_502_17@ ............
@H_502_17@ //最后将构造好的类对象ctor(本质上是一个函数对象)赋值给我们声明的类名 @H_502_17@ dojo.setObject(className,ctor)@H_502_17@ @H_502_17@ .............
@H_403_0@所以这里其实可以知道,在dojo中declare的类,其本质是函数对象,其实他就是如下代码:ctor = function(){};
ctor.prototype = props 属性定义.....
类名变量{eval(className)} = ctor;
@H_403_0@3> 下面我们来看一看第二个参数,关系着dojo的继承机制:(具体可见加粗的字体说明)..................@H_502_17@ @H_502_17@ if(opts.call(superclass) == "[object Array]"){@H_502_17@ //多个基类时,整理基类数组 @H_502_17@ bases = c3mro(superclass);@H_502_17@ t = bases[0];@H_502_17@ mixins = bases.length - t;
//基类数组里的第一个对象 @H_502_17@ superclass = bases[mixins];@H_502_17@ }else{
//只有一个基类的情况 @H_502_17@ bases = [0];@H_502_17@ if(superclass){@H_502_17@ if(opts.call(superclass) == "[object Function]"){@H_502_17@ t = superclass._Meta;@H_502_17@ bases = bases.concat(t ? t.bases : superclass);@H_502_17@ }else{@H_502_17@ err("base class is not a callable constructor.");@H_502_17@ }@H_502_17@ }else if(superclass !== null){@H_502_17@ err("unknown base class. Did you use dojo.require to pull it in?")@H_502_17@ }@H_502_17@ }
//开始构造基类属性,合并他们的prototype内容 @H_502_17@ if(superclass){@H_502_17@ for(i = mixins - 1;; --i){
//拷贝prototype ,superclass为基类数组里的第一个函数对象,forceNew之后变成含有superclass的prototype 属性的一个对象 @H_502_17@ proto = forceNew(superclass);@H_502_17@ if(!i){@H_502_17@ // stop if nothing to add (the last base)@H_502_17@ break;@H_502_17@ }@H_502_17@ // mix in properties@H_502_17@ t = bases[i];
//合并所有基类的prototype,作为后面子类的prototype,实现属性共享 @H_502_17@ (t._Meta ? mixOwn : mix)(proto,t.prototype);@H_502_17@ //构造 基于合并prototype的基类临时函数对象 @H_502_17@ ctor = new Function;@H_502_17@ ctor.superclass = superclass;@H_502_17@ ctor.prototype = proto;
@H_502_17@ superclass = proto.constructor = ctor;
//如上这行代码用于调整所构造的ctor的constructor,保持一致性,然后修改superclass用于后续迭代,其实将这一行写成如下两行更通俗易懂:
// ctor.prototype.constructor = ctor;
@H_403_0@ // superclass = ctor;@H_502_17@ }@H_502_17@ }else{@H_502_17@ proto = {};@H_502_17@ }@H_502_17@ @H_502_17@ .................
@H_403_0@这里我们只是粗略的把dojo.declare的主线路大致过了一遍,后面附上了dojo.declare的源代码(基于dojo1.5),大家可以参考一下。关于dojo.declare的实现细节还有很多很多值得讨论的地方,但是这里由于篇幅过长,所以关于dojo的面向对象机制的详细实现我们以后再继续讨论,这里就不做详细说明了。 @H_403_0@dojo.declare 是和 dojo.require(类似java的import) 以及 dojo.provide 联合起来一起使用的,主要由这3个函数将javascript封装成了一个接近面向对象的dojo语言模式,这也是dojo比起其他web控件库比较独到的一大特点。 @H_403_0@另外强调一点:在dojo.declare方法里还实现了一种比较有用的链式机制,通过定义“-chains-”变量来使用,有兴趣可以研究一下,这里不做过多说明。 @H_403_0@附上dojo.declare源代码:(dojo1.5) @H_403_0@d.declare = function(className,superclass,props){@H_502_17@ // crack parameters@H_502_17@ if(typeof className != "string"){@H_502_17@ props = superclass;@H_502_17@ superclass = className;@H_502_17@ className = "";@H_502_17@ }@H_502_17@ props = props || {};@H_502_17@ @H_502_17@ var proto,i,t,ctor,name,bases,chains,mixins = 1,parents = superclass;@H_502_17@ @H_502_17@ // build a prototype@H_502_17@ if(opts.call(superclass) == "[object Array]"){@H_502_17@ // C3 MRO@H_502_17@ bases = c3mro(superclass);@H_502_17@ t = bases[0];@H_502_17@ mixins = bases.length - t;@H_502_17@ superclass = bases[mixins];@H_502_17@ }else{@H_502_17@ bases = [0];@H_502_17@ if(superclass){@H_502_17@ if(opts.call(superclass) == "[object Function]"){@H_502_17@ t = superclass._Meta;@H_502_17@ bases = bases.concat(t ? t.bases : superclass);@H_502_17@ }else{@H_502_17@ err("base class is not a callable constructor.");@H_502_17@ }@H_502_17@ }else if(superclass !== null){@H_502_17@ err("unknown base class. Did you use dojo.require to pull it in?")@H_502_17@ }@H_502_17@ }@H_502_17@ if(superclass){@H_502_17@ for(i = mixins - 1;; --i){@H_502_17@ proto = forceNew(superclass);@H_502_17@ if(!i){@H_502_17@ // stop if nothing to add (the last base)@H_502_17@ break;@H_502_17@ }@H_502_17@ // mix in properties@H_502_17@ t = bases[i];@H_502_17@ (t._Meta ? mixOwn : mix)(proto,t.prototype);@H_502_17@ // chain in new constructor@H_502_17@ ctor = new Function;@H_502_17@ ctor.superclass = superclass;@H_502_17@ ctor.prototype = proto;@H_502_17@ superclass = proto.constructor = ctor;@H_502_17@ }@H_502_17@ }else{@H_502_17@ proto = {};@H_502_17@ }@H_502_17@ // add all properties@H_502_17@ safeMixin(proto,props);@H_502_17@ // add constructor@H_502_17@ t = props.constructor;@H_502_17@ if(t !== op.constructor){@H_502_17@ t.nom = cname;@H_502_17@ proto.constructor = t;@H_502_17@ }@H_502_17@ @H_502_17@ // collect chains and flags@H_502_17@ for(i = mixins - 1; i; --i){ // intentional assignment@H_502_17@ t = bases[i]._Meta;@H_502_17@ if(t && t.chains){@H_502_17@ chains = mix(chains || {},t.chains);@H_502_17@ }@H_502_17@ }@H_502_17@ if(proto["-chains-"]){@H_502_17@ chains = mix(chains || {},proto["-chains-"]);@H_502_17@ }@H_502_17@ @H_502_17@ // build ctor@H_502_17@ t = !chains || !chains.hasOwnProperty(cname);@H_502_17@ bases[0] = ctor = (chains && chains.constructor === "manual") ? simpleConstructor(bases) :@H_502_17@ (bases.length == 1 ? singleConstructor(props.constructor,t));@H_502_17@ @H_502_17@ // add Meta information to the constructor@H_502_17@ ctor._Meta = {bases: bases,@H_502_17@ parents: parents,ctor: props.constructor};@H_502_17@ ctor.superclass = superclass && superclass.prototype;@H_502_17@ ctor.extend = extend;@H_502_17@ ctor.prototype = proto;@H_502_17@ proto.constructor = ctor;@H_502_17@ @H_502_17@ // add "standard" methods to the prototype@H_502_17@ proto.getInherited = getInherited;@H_502_17@ proto.inherited = inherited;@H_502_17@ proto.isInstanceOf = isInstanceOf;@H_502_17@ @H_502_17@ // add name if specified@H_502_17@ if(className){@H_502_17@ proto.declaredClass = className;@H_502_17@ d.setObject(className,ctor);@H_502_17@ }@H_502_17@ @H_502_17@ // build chains and add them to the prototype@H_502_17@ if(chains){@H_502_17@ for(name in chains){@H_502_17@ if(proto[name] && typeof chains[name] == "string" && name != cname){@H_502_17@ t = proto[name] = chain(name,chains[name] === "after");@H_502_17@ t.nom = name;@H_502_17@ }@H_502_17@ }@H_502_17@ }@H_502_17@ // chained methods do not return values@H_502_17@ // no need to chain "invisible" functions@H_502_17@ @H_502_17@ return ctor; // Function };