昨天扔了一堆JavaScript类'继承'的代码,这些代码其实并不是所有的都能正常的执行。不是我不原意写出都能好好执行的继承类代码,而是这些方法本身就各自有自己的优缺点。下面我分别说它们的原理和使用时注意事项。
构造继承法的原理:
构造继承法关键代码是 function ArrayList01()中的:
这里的base不是C#派生类中的base那个概念,完全就是一个任意的JavaScript变量名。调用 this.base();其实就是执行的CollectionBase();,不过 不是 new CollectionBase();哦!没有 new基类那么怎么得到CollectionBase中的方法和属性呢?这里使用了 this作用域的一个hack,'欺骗'了脚本引擎。当我们从类ArrayList01的构造函数中调用 this.base();时,在基类CollectionBase中的 this就是ArrayList01的一个实例,于是执行CollectionBase的构造函数,就动态的把基类的成员和方法attach到ArrayList01实例中了。构造法的问题也就是从这里产生了,注意哦。
构造继承法的缺陷:
第一个问题,关键是出在上面那两段代码上,因为始终没有 new基类呀。这样带来的问题就是不能把基类CollectionBase中的原型属性和方法attach到ArrayList01的实例中,啊,这样还算是什么继承啊?! 所以在 上篇文章中我为了不改动CollectionBase类,而单独为其实现了一个Add()方法(只是为了统一示例代码而已)。解决这个缺陷的方法其实也很简单,就是要求基类不能使用prototype来导入属性和方法,而要把所有的属性和方法都写到构造函数中去,分别是: this.Attribute = ...; 和 this.Method = function() {...};这种形式。
第二个问题,是 this.base = CollectionBase;和 this.base();必须写在派生类构造函数的最开头(不是一定要是第一行和第二行,而是它们的前面不能有 this.xxx这种定义为ArrayList01导入的任何属性或方法),因为调用 this.base();时会向 this(ArrayList01的一个实例)中注入基类的属性和方法,如果基类中有和 this中已导入的属性和方法重名的,就自动覆盖掉子类中的方法。
第三个问题,是子类也不能使用prototype来导入属性和方法,这和问题二中的重名覆盖道理一样,由于prototype导入的属性和方法在子类一 new的时候就生成了,所以也存在和基类重名而被覆盖的潜在错误威胁。解决办法,和基类编写规则一样不要使用prototype,并且将子类的属性和方法定义代码放在导入继承的代码( this.base = CollectionBase; this.base();)之后。
构造继承法的示例:
示例运行结果为:
小结一下:JavaScript的构造继承法其实看起来还是比较直观的,因为子类构造函数中有对基类构造函数的调用,似乎在语法上还比较容易被接受。可是由于没有 new基类,带来了不能获得prototype导入的属性和方法的缺陷。解决这个缺陷的方法虽然不难,可是由此而给基类编写限制一个凌驾于JavaScript语法规范的规则,可操作性不是很好。子类也不能利用prototype特性来导入属性和方法,会与基类之间存在潜在的重名覆盖问题。所以对于复杂的基类,不推荐这种继承方法,因为类复杂了,使用prototype可以规范类代码,使类的定义看起来比较的舒服 。
构造继承法的原理:
构造继承法关键代码是 function ArrayList01()中的:
this.base = CollectionBase;
this.base();
this.base();
这里的base不是C#派生类中的base那个概念,完全就是一个任意的JavaScript变量名。调用 this.base();其实就是执行的CollectionBase();,不过 不是 new CollectionBase();哦!没有 new基类那么怎么得到CollectionBase中的方法和属性呢?这里使用了 this作用域的一个hack,'欺骗'了脚本引擎。当我们从类ArrayList01的构造函数中调用 this.base();时,在基类CollectionBase中的 this就是ArrayList01的一个实例,于是执行CollectionBase的构造函数,就动态的把基类的成员和方法attach到ArrayList01实例中了。构造法的问题也就是从这里产生了,注意哦。
构造继承法的缺陷:
第一个问题,关键是出在上面那两段代码上,因为始终没有 new基类呀。这样带来的问题就是不能把基类CollectionBase中的原型属性和方法attach到ArrayList01的实例中,啊,这样还算是什么继承啊?! 所以在 上篇文章中我为了不改动CollectionBase类,而单独为其实现了一个Add()方法(只是为了统一示例代码而已)。解决这个缺陷的方法其实也很简单,就是要求基类不能使用prototype来导入属性和方法,而要把所有的属性和方法都写到构造函数中去,分别是: this.Attribute = ...; 和 this.Method = function() {...};这种形式。
第二个问题,是 this.base = CollectionBase;和 this.base();必须写在派生类构造函数的最开头(不是一定要是第一行和第二行,而是它们的前面不能有 this.xxx这种定义为ArrayList01导入的任何属性或方法),因为调用 this.base();时会向 this(ArrayList01的一个实例)中注入基类的属性和方法,如果基类中有和 this中已导入的属性和方法重名的,就自动覆盖掉子类中的方法。
第三个问题,是子类也不能使用prototype来导入属性和方法,这和问题二中的重名覆盖道理一样,由于prototype导入的属性和方法在子类一 new的时候就生成了,所以也存在和基类重名而被覆盖的潜在错误威胁。解决办法,和基类编写规则一样不要使用prototype,并且将子类的属性和方法定义代码放在导入继承的代码( this.base = CollectionBase; this.base();)之后。
构造继承法的示例:
<
script
language
="JavaScript"
>
document.write('构造继承法: < br > ');
var arrayList11 = new ArrayList01();
arrayList11.Add('a');
arrayList11.Add('b');
arrayList11.foo();
var arrayList12 = new ArrayList01();
arrayList12.Add('a');
arrayList12.Add('b');
arrayList12.Add('c');
arrayList12.foo();
</ script >
document.write('构造继承法: < br > ');
var arrayList11 = new ArrayList01();
arrayList11.Add('a');
arrayList11.Add('b');
arrayList11.foo();
var arrayList12 = new ArrayList01();
arrayList12.Add('a');
arrayList12.Add('b');
arrayList12.Add('c');
arrayList12.foo();
</ script >
示例运行结果为:
构造继承法:
[class ArrayList01]: 2: a,b
[class ArrayList01]: 3: a,b,c
[class ArrayList01]: 2: a,b
[class ArrayList01]: 3: a,b,c
小结一下:JavaScript的构造继承法其实看起来还是比较直观的,因为子类构造函数中有对基类构造函数的调用,似乎在语法上还比较容易被接受。可是由于没有 new基类,带来了不能获得prototype导入的属性和方法的缺陷。解决这个缺陷的方法虽然不难,可是由此而给基类编写限制一个凌驾于JavaScript语法规范的规则,可操作性不是很好。子类也不能利用prototype特性来导入属性和方法,会与基类之间存在潜在的重名覆盖问题。所以对于复杂的基类,不推荐这种继承方法,因为类复杂了,使用prototype可以规范类代码,使类的定义看起来比较的舒服 。
应用场景:小规模类之间的继承,基类和子类的属性方法在5-8个。还有就是以构造函数中赋值方式导入类的属性和方法,而不用prototype导入的类编写习惯的时候。
本文转自博客园鸟食轩的博客,原文链接:http://www.cnblogs.com/birdshome/,如需转载请自行联系原博主。