`

13、JavaScript继承实现(二) —— zInherit、xbObjects

阅读更多
  • zInherit

利用zInherit库(可以从http://www.nczonline.net/downloads 处下载),不必使用原型链,也可实现方法继承。
Inherit库给Object类添加了两个方法,inheritFrom()和instanceOf()。inheritFrom()方法负担重任,负责复制指定类的所有方法。下面一行代码用原型链使ClassB继承ClassA的方法:
ClassB.prototype = new ClassA();
可用下面代码替换上面的代码:
ClassB.prototype.inheritFrom(ClassA);
inheritFrom() 方法接受一个参数,即要复制的方法所属的类。注意,与原型链相对的是,这种方式并未真正创建要继承的类的实例(具体请参考zInherit源码),这样更安全,开发者也无需担心构造函数的参数。

instanceOf() 方法是instanceof运算符的替代品。因为这种方式根本不使用原型链,所以这行代码无效:
base3 instanceof Base1
但instanceOf()方法弥补了这项损失,与inheritFrom()一起使用,可以跟踪所有的超类:
base3.instanceOf(Base1);

zInherit多层继承具体示例代码如下:

function Base1(base1Name) {    
    this.base1Name = base1Name;    
    if (typeof Base1._initialized == "undefined") {    
        Base1.prototype.getBase1 = function () {    
            alert(this.base1Name);    
        };    
        Base1._initialized = true;    
    }    
}     
//Base2继承Base1     
function Base2(base1Name, base2Name) {    
    this.base2Name = base2Name;     
    //使用call方式对属性继承     
    Base1.call(this, base1Name);    
    if (typeof Base2._initialized == "undefined") {     
        //使用zinherit方式对方法时行复制继承     
        Base2.prototype.inheritFrom(Base1);    
        Base2.prototype.getBase2 = function () {    
            alert(this.base2Name);    
        };    
        Base2._initialized = true;    
    }    
}     
//Base3继承Base2     
function Base3(base1Name, base2Name, base3Name) {    
    this.base3Name = base3Name;     
    //使用call方式对属性继承    
    Base2.call(this, base1Name, base2Name);    
    if (typeof Base3._initialized == "undefined") {     
        //使用zinherit方式对方法时行复制继承     
        Base3.prototype.inheritFrom(Base2);    
        Base3.prototype.getBase3 = function () {    
            alert(this.base3Name);    
        };    
        Base3._initialized = true;    
    }    
}    
var base3 = new Base3("base1Name", "base2Name", "base3Name");     
//Base3可以访问从父类Base2继承过来的方法     
base3.getBase2();//base2Name    
//Base3可以访问从超类Base1继承过来的方法     
base3.getBase1();//base1Name     
//当然更可以访问自己的方法     
base3.getBase3();//base1Name    
//因为zinherit方式未采用原型链的方法,所以不支持instanceof     
alert(base3 instanceof Base1);//false    
//但可以用zinherit的instanceOf方法来避免该问题     
alert(base3.instanceOf(Base1));//true     
alert(base3.instanceOf(Base2));//true     
alert(base3.instanceOf(Base3));//true    

另外:原型链不能实现动态原型主旨,即把类的所有代码放置在它的构造函数中。zInherit库修正了这个问题,它允许在构造函数内部调用 inheritFrom()方法,使用inheritFrom()方法时,并未重写prototype对象,只是为其加入方法而已(从zInherit源码分析可知)。使用这种方法,即可避开原型链的限制,实现动态原型本意。所以zInherit支持动态原型,从上面的示例就可以看得出来。

 

zInherit的另一个好处就是除了支持多层继承、动态原型法,还支持多重继承,即一个类可以继承多个类,这些类是同一级别的。

zInherit多重继承示例如下,显示的结果与多层继承一样:  

function Base1(base1Name) {    
    this.base1Name = base1Name;    
    if (typeof Base1._initialized == "undefined") {    
        Base1.prototype.getBase1 = function () {    
            alert(this.base1Name);    
        };    
        Base1._initialized = true;    
    }    
}    
function Base2(base2Name) {    
    this.base2Name = base2Name;    
    if (typeof Base2._initialized == "undefined") {    
        Base2.prototype.getBase2 = function () {    
            alert(this.base2Name);    
        };    
        Base2._initialized = true;    
    }    
}     
//多重继承:Base3继承Base1与Base2     
function Base3(base1Name, base2Name, base3Name) {    
    this.base3Name = base3Name;     
    //使用call方式对属性继承     
    Base1.call(this, base1Name);    
    Base2.call(this, base2Name);    
    if (typeof Base3._initialized == "undefined") {    
        //使用zinherit方式对方法时行复制继承     
        Base3.prototype.inheritFrom(Base1);    
        Base3.prototype.inheritFrom(Base2);    
        Base3.prototype.getBase3 = function () {    
            alert(this.base3Name);    
        };    
        Base3._initialized = true;    
    }    
}    
var base3 = new Base3("base1Name", "base2Name", "base3Name");     
//Base3可以访问从父类Base2继承过来的方法     
base3.getBase2();//base2Name    
//Base3可以访问从超类Base1继承过来的方法     
base3.getBase1();//base1Name    
//当然更可以访问自己的方法     
base3.getBase3();//base3Name    
//因为zinherit方式未采用原型链的方法,所以不支持instanceof     
alert(base3 instanceof Base1);//false    
//但可以用zinherit的instanceOf方法来避免该问题     
alert(base3.instanceOf(Base1));//true     
alert(base3.instanceOf(Base2));//true     
alert(base3.instanceOf(Base3));//true    

 

zinherit.js源码分析

/**    
* Inherits properties and methods from the given class.    
* @scope public    
* @param fnClass The constructor function to inherit from.    
*/   
Object.prototype.inheritFrom = function (fnClass) {    
    /**    
    * Inherits all classes going up the inheritance chain recursively.    
    * @param fnClass The class to inherit from.    
    * @param arrClasses The array of classes to build up.    
    * @scope private    
    */   
    function inheritClasses(fnClass, arrClasses) {    
        /*   
        把父类构造函数放入到子类构造函数的私有属性数组中,用数组实现多继承,即可以继承多个。   
        此数组里最后存放了所有父类及超类的构造函数,如果子类继承了多个类,则此数组会存储多个构造函数。    
        */   
        arrClasses.push(fnClass);    
        //如果父类构造函数继承了其他类,则递归,这样可以实现多层继承     
        if (typeof fnClass.__superclasses__ == "object") {    
            for (var i = 0; i < fnClass.__superclasses__.length; i++) {    
                //fnClass.__superclasses__[i]为父类的构造函数,如果父类继承了多个类,    
                //则fnClass.__superclasses__.length会大于1     
                inheritClasses(fnClass.__superclasses__[i], arrClasses);    
            }    
        }    
    }     
    //this为子类构造函数的原型    
    //this.constructor为子类构造函数    
    //this.constructor.__superclasses__为子类构造函数的私有属性数组     
    if (typeof this.constructor.__superclasses__ == "undefined") {     
        //这里的数组用来实现instanceOf用的     
        this.constructor.__superclasses__ = new Array();    
    }     
    //fnClass为父类构造函数     
    inheritClasses(fnClass, this.constructor.__superclasses__);    
      
    //----上面的操作是为了instanceOf方法应用设计的,下面才是方法继承    
    //把父类原型里存储的方法复制到子类构造函数原型中(注:这里是方法地址的复制),而不是真真的方法复制     
    for (prop in fnClass.prototype) {    
        if (typeof fnClass.prototype[prop] == "function") {     
            //this为子类构造函数的原型实例,这里把父类原型实例中的属性(一般指方法属性,但不排除字    
            //段属性)一一复制到子类构造函数的原型实例中去。而不是采用原型链的方式:Base3.prototype=new Base2();     
            this[prop] = fnClass.prototype[prop];    
        }    
    }    
};    
/**    
   * Determines if the given object is an instance of a given class.    
   * This method is necessary because using {@link #inheritFrom} renders    
   * the JavaScript <code>instanceof</code> operator useless.    
   * @param fnClass The constructor function to test.    
   * @return True if the object is an instance of the class, false if not.    
   * @scope public    
   */   
Object.prototype.instanceOf = function (fnClass) {     
   //如果是base3.instanceOf(Base3)时     
    if (this.constructor == fnClass) {    
        return true;     
    //如果是采用zinherit继承方式时,则this.constructor.__superclasses__为数组对象     
    } else {    
        if (typeof this.constructor.__superclasses__ == "object") {     
            //再进一步判断,如:调用base3.instanceOf(Base1)时     
            for (var i = 0; i < this.constructor.__superclasses__.length; i++) {    
                if (this.constructor.__superclasses__[i] == fnClass) {    
                    return true;    
                }    
            }    
            return false;    
        } else {    
            return false;    
        }    
    }    
};   
  • xbObjects

Netscape 公司的 Bob Clary 2001 Netscape 6 Mozilla 0.6 )发布时编写而成。它支持从那时起的所有 Mozilla 版本及其他现代浏览器( IE Opera Safari )。可以从 http://archive.bclary.com/xbProjects-docs/xbObject/ 处下载。

 

   支持继承,还支持方法的重载和调用超类方法的能力。

   

 第一步必须注册,此时,需定义它是由哪个类继承而来:

这里,子类和超类名都以字符串形式传进来,而不是指向它们的构造函数的指针这个调用必须放在指定子类的构造 函数前

 如果新的类未继承任何类,调用 registerClass() 时也可以只用第一个参数。

<script type="text/javascript"></script>

 

 

第二步 ,在构造函数内调用 defineClass() 方法 ,传给它类名及被 Clary 称为 原型函数 prototype function )的指针,该函数用于初始化对象的所有属性和方法 ,例如:

  

第三步为该类创建 init() 方法 。该方法负责设置该类的所有属性 ,它必须接受与构造函数相同的参数。作为一种规定, init() 方法总是在 defineClass() 方法后调用 。例如:

init() 方法中调用的 parentMethod() 方法。 xbObjects 以这种方式允许类调用它的超类的方法 parentMethod() 方法接受任意多个参数,但第一个参数总是要调用的父类方法的名字(该参数必须是字符串,而不是函数指针),所有其他参数都被传给父类的方法。

在这个例子中,首先调用 init() 方法,这是 xbObjects 运行所必需的。即使 ClassA 未注册超类, xbObejcts 都会为它创建一个所有类的默认超类,即超类方法 init() 所属的类。

 

第四步 也是最后一步,在原型函数内添加其他类的方法

xbObjects多层继承示例如下:     

//注册类     
_classes.registerClass("Base1");    
function Base1(base1Name) {     
    //基于原型来初始化类的属性与方法     
    function prototypeFunction() {     
        //init方法设置该类的所有属性,它必须接收与构造函数相同的参数签名     
        Base1.prototype.init = function (base1Name) {     
            //...     
            this.parentMethod("init");    
            this.base1Name = base1Name;    
        };     
        //设置方法     
        Base1.prototype.getBase1 = function () {    
            alert(this.base1Name);    
        };    
    }     
    //定义类,用来初始化对象所有属性与方法     
    _classes.defineClass("Base1", prototypeFunction);    
    this.init(base1Name);    
}     
//Base2继承Base1     
_classes.registerClass("Base2", "Base1");    
function Base2(base1Name, base2Name) {    
    function prototypeFunction() {     
        //init方法接收与构造函数相同的参数签名     
        Base2.prototype.init = function (base1Name, base2Name) {    
            //...     
            this.parentMethod("init", base1Name);    
            this.base2Name = base2Name;    
        };     
        //设置方法     
        Base2.prototype.getBase2 = function () {    
            alert(this.base2Name);    
        };    
    }     
    //定义类,用来初始化对象所有属性与方法     
    _classes.defineClass("Base2", prototypeFunction);    
    this.init(base1Name, base2Name);    
}     
//Base3继承Base2     
_classes.registerClass("Base3", "Base2");    
function Base3(base1Name, base2Name, base3Name) {    
    function prototypeFunction() {    
        Base3.prototype.init = function (base1Name, base2Name, base3Name) {     
            //...     
            this.parentMethod("init", base1Name, base2Name);    
            this.base3Name = base3Name;    
        };     
        //设置方法     
        Base3.prototype.getBase3 = function () {    
            alert(this.base3Name);    
        };    
    }     
    //定义类,用来初始化对象所有属性与方法     
    _classes.defineClass("Base3", prototypeFunction);    
    this.init(base1Name, base2Name, base3Name);    
}    
var base3 = new Base3("base1Name", "base2Name", "base3Name");     
//Base3可以访问从父类Base2继承过来的方法     
base3.getBase2();//base2Name    
//Base3可以访问从超类Base1继承过来的方法     
base3.getBase1();//base1Name    
//当然更可以访问自己的方法     
base3.getBase3();//base3Name    
//因为xbObjects方式未采用原型链的方法,所以不支持instanceof     
alert(base3 instanceof Base1);//false    
//但可以用zinherit的instanceOf方法来避免该问题     
alert(base3.isInstanceOf(Base1));//true     
alert(base3.isInstanceOf(Base2));//true     
alert(base3.isInstanceOf(Base3));//true    

 

注:上面this.parentMethod("init"...)前最好不要加上中文注释,否则可能出问题。

 

附:zInherit、xbObjects

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics