现在的位置: 首页 > 综合 > 正文

Dojo面向对象机制深入剖析

2013年09月17日 ⁄ 综合 ⁄ 共 7982字 ⁄ 字号 评论关闭

 

Dojo是一个基于javascript语言的web控件库,要了解dojo的面向对象机制,我们可以首先来回顾一下javascript本身的基本面向对象机制:

 

首先,javascript主要通过函数来模拟面向对象机制,如:

function func1(){};

func1();  //函数调用

new func1();  //实例化的对象

上述两种方式其函数内部的this对象也是不一样的。

 

再者,关于javascript的最基本的继承方式主要有两种,(当然还有更复杂的,这里只列出最基本的两种方式)

1. 对象冒充方式:

function
ClassA(sColor) {
    this.color = sColor;
    this.xxx = xxx;
  
......
}

function
ClassB(sColor, name) {
    this.newMethod = ClassA;
   
this.newMethod(sColor);
    delete this.newMethod;
   ......
}  

或者:(这里ClassA同上)

function
ClassB(sColor, sName)  { 
    ClassA.call(this, sColor); 
//或者apply


 
    this.name =  sName; 
    this.sayName =
function() { 
        alert(this.name); 
    }; 

 

 

 

2. 原型链式继承:

//基类

function ClassA()
{ } 
ClassA.... = .....; 
//子类 
function ClassB() { } 

//通过修改prototype来达到继承目的
ClassB.prototype = new ClassA(); 


//定义新属性与方法 
ClassB.... = .....;

//保证constructor的一致性(修改prototype后其constructor变为ClassA,应该是ClassB)

ClassB.prototype.constructor =
ClassB;

 

 

上述两种方法各有优缺点:对象冒充方式效率低,而且必须使用构造函数方式但是能支持多继承,而prototype方式很灵活但不支持多继承。于是有了如下混合方式:

function
ClassA(sColor) { } 
ClassA.... = ..... //设置属性

function ClassB() { 

    ClassA.call(this, sColor);   //对象冒充方式  继承属性 

   
this.name = sName; 

ClassB.prototype = new ClassA();  //原型链方式
继承方法 


ClassB.prototype.sayName = function() { 
   
alert(this.name); 

 

 

下面我们来看看dojo是如何实现继承机制的:

下面是dojo中声明类和使用类的一个简单示例,可以看出,其核心在一个dojo.declare函数上:

 

我们来看看dojo.declare函数:

1> 先看第一个参数:className参数,可以见如下相关代码:

......

// add name if
specified
if(className){
    proto.declaredClass = className;
   
d.setObject(className, ctor);

}

......

 

其中ctor是在该方法内构造的一个函数,其实是该声明类的定义,其本身是一个函数对象,里面有很多成员:包括属性定义,函数定义,关于这个的详细介绍这个后面会涉及到。这里主要通过dojo.setObject将该构造的函数对象赋给名为className的变量,如dijit.WidgetSet.

所以这里相当于这样写:

ctor = function(){....};

Dijit.WidgetSet = ctor;

 

另外强调一下,这里的proto就是ctor的prototype.

 

 

2> 我们再来看看第三个参数:props,可以见如下代码:(具体可见加粗的字体说明)

............

proto = {};

//复制一份类的定义对象的内容,见上面Dijit.Calendar的templateString,
value, buildRendering等等属性和方法.

safeMixin(proto, props);

//
开始构造ctor,即构造类的定义,可以看到里面还有很多dojo自己添加的属性


t = !chains ||
!chains.hasOwnProperty(cname);

//以下三个对象singleConstructor/simpleConstructor/chainedConstructor  
都是

是一个函数对象,其返回值也是一个函数对像,这些返回的函数对象内部,都会包含一些默认操作,包括初始化,调用ctor及其基类的ctor等等,具体细节可以参见上述三个函数的定义,可以看出模拟dojo的类的函数在初始化时都做了些什么,很值得研究,为了是篇幅不要太长,这里暂时不做过于细致的深入。

bases[0] = ctor = (chains && chains.constructor === "manual") ?
simpleConstructor(bases) :
    (bases.length == 1 ?
singleConstructor(props.constructor, t) : chainedConstructor(bases,
t));

// add meta information to the constructor

//这里的ctor属性很关键,在上面提到的

singleConstructor/simpleConstructor/chainedConstructor函数里面会调用到,这也是为什么dojo.declare的“类”在初始化时(new操作)会自动调用constructor方法的原因!!!

ctor._meta  = {bases: bases, hidden: props, chains: chains,
    parents:
parents, ctor: props.constructor
};
ctor.superclass =
superclass && superclass.prototype;
ctor.extend = extend;

 

//这里是比较关键的操作了,将定义的属性全部作为prototype的属性,注意我们这里先不谈继承,而且这里不是在继承,请和前面的javascript继承区分开

ctor.prototype =
proto;

//由于prototype被修改,所以需要重新赋值以保持prototype的constructor一致性

proto.constructor = ctor;

//
添加dojo里面比较常用的方法,注意:这些方法被直接加到了prototype中!


proto.getInherited =
getInherited;
proto.inherited = inherited;
proto.isInstanceOf =
isInstanceOf;

............

//最后将构造好的类对象ctor(本质上是一个函数对象)赋值给我们声明的类名

dojo.setObject(className, ctor)

.............

 

所以这里其实可以知道,在dojo中declare的类,其本质是函数对象,其实他就是如下代码:

ctor =
function(){};

ctor.prototype =
props 属性定义.....

类名变量{eval(className)} = ctor;

 

 

3> 下面我们来看一看第二个参数,关系着dojo的继承机制:(具体可见加粗的字体说明)

..................

if(opts.call(superclass)
== "[object Array]"){
    //多个基类时,整理基类数组

    bases =
c3mro(superclass);
    t = bases[0];
    mixins = bases.length - t;

   
//基类数组里的第一个对象

    superclass = bases[mixins];
}else{

   
//只有一个基类的情况

    bases = [0];
    if(superclass){
   
    if(opts.call(superclass) == "[object Function]"){
            t =
superclass._meta;
            bases = bases.concat(t ? t.bases :
superclass);
        }else{
            err("base class is not a callable
constructor.");
        }
    }else if(superclass !== null){
       
err("unknown base class. Did you use dojo.require to pull it in?")
   
}
}

//开始构造基类属性,合并他们的prototype内容

if(superclass){
    for(i = mixins - 1;; --i){

       
//拷贝prototype

superclass为基类数组里的第一个函数对象,forceNew之后变成含有superclass的prototype  
属性的一个对象


        proto = forceNew(superclass);
       
if(!i){
            // stop if nothing to add (the last base)
           
break;
        }
        // mix in properties
        t = bases[i];

       
//合并所有基类的prototype,作为后面子类的prototype,实现属性共享

        (t._meta
? mixOwn : mix)(proto, t.prototype);
        //构造
基于合并prototype的基类临时函数对象


        ctor = new Function;
       
ctor.superclass = superclass;
        ctor.prototype = proto;

       
   
    superclass = proto.constructor = ctor;

       
//如上这行代码用于调整所构造的ctor的constructor,保持一致性,然后修改superclass用于后续迭代,其实将这一行写成如下两行更通俗易懂:

       
//      ctor.prototype.constructor = ctor;

                //      superclass = ctor;

   
}
}else{
    proto = {};
}

.................

 

 

 

这里我们只是粗略的把dojo.declare的主线路大致过了一遍,后面附上了dojo.declare的源代码(基于dojo1.5),大家可以参考一下。关于dojo.declare的实现细节还有很多很多值得讨论的地方,但是这里由于篇幅过长,所以关于dojo的面向对象机制的详细实现我们以后再继续讨论,这里就不做详细说明了。

 

dojo.declare 是和 dojo.require(类似java的import) 以及 dojo.provide
联合起来一起使用的,主要由这3个函数将javascript封装成了一个接近面向对象的dojo语言模式,这也是dojo比起其他web控件库比较独到的一大特点。

 

另外强调一点:在dojo.declare方法里还实现了一种比较有用的链式机制,通过定义“-chains-”变量来使用,有兴趣可以研究一下,这里不做过多说明。

 

 

 

 

 

 

 

 

附上dojo.declare源代码:(dojo1.5)

 

d.declare =
function(className, superclass, props){
        // crack parameters
   
    if(typeof className != "string"){
            props = superclass;
   
        superclass = className;
            className = "";
       
}
        props = props || {};


        var proto, i, t, ctor, name, bases,
chains, mixins = 1, parents = superclass;


        // build a
prototype
        if(opts.call(superclass) == "[object Array]"){
       
    // C3 MRO
            bases = c3mro(superclass);
            t =
bases[0];
            mixins = bases.length - t;
            superclass =
bases[mixins];
        }else{
            bases = [0];
           
if(superclass){
                if(opts.call(superclass) == "[object
Function]"){
                    t = superclass._meta;
                   
bases = bases.concat(t ? t.bases : superclass);
                }else{
   
                err("base class is not a callable constructor.");
           
    }
            }else if(superclass !== null){
               
err("unknown base class. Did you use dojo.require to pull it in?")
       
    }
        }
        if(superclass){
            for(i = mixins -
1;; --i){
                proto = forceNew(superclass);
               
if(!i){
                    // stop if nothing to add (the last base)
   
                break;
                }
                // mix in
properties
                t = bases[i];
                (t._meta ? mixOwn
: mix)(proto, t.prototype);
                // chain in new
constructor
                ctor = new Function;
               
ctor.superclass = superclass;
                ctor.prototype = proto;
   
            superclass = proto.constructor = ctor;
            }
       
}else{
            proto = {};
        }
        // add all
properties
        safeMixin(proto, props);
        // add
constructor
        t = props.constructor;
        if(t !==
op.constructor){
            t.nom = cname;
            proto.constructor
= t;
        }


        // collect chains and flags
       
for(i = mixins - 1; i; --i){ // intentional assignment
            t =
bases[i]._meta;
            if(t && t.chains){
               
chains = mix(chains || {}, t.chains);
            }
        }
       
if(proto["-chains-"]){
            chains = mix(chains || {},
proto["-chains-"]);
        }


        // build ctor
        t = !chains ||
!chains.hasOwnProperty(cname);
        bases[0] = ctor = (chains &&
chains.constructor === "manual") ? simpleConstructor(bases) :
           
(bases.length == 1 ? singleConstructor(props.constructor, t) :
chainedConstructor(bases, t));


        // add meta information to the
constructor
        ctor._meta  = {bases: bases, hidden: props, chains:
chains,
            parents: parents, ctor: props.constructor};
       
ctor.superclass = superclass && superclass.prototype;
       
ctor.extend = extend;
        ctor.prototype = proto;
       
proto.constructor = ctor;


        // add "standard" methods to the
prototype
        proto.getInherited = getInherited;
       
proto.inherited = inherited;
        proto.isInstanceOf =
isInstanceOf;


        // add name if specified
       
if(className){
            proto.declaredClass = className;
           
d.setObject(className, ctor);
        }


        // build
chains and add them to the prototype
        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;
                }
            }
        }
       
// chained methods do not return values
        // no need to chain
"invisible" functions


        return ctor;    // Function
   
};

抱歉!评论已关闭.