利用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; } } };
由 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