- <html>
- <head>
- <title>Example-21.14 构造继承法</title>
- </head>
- <body>
- <script>
- <!--
- function dwn(s){
- document.write(s + "<br/>");
- }
- //定义一个Collection类型
- function Collection(size){
- this.size = function(){ return size; }; //公有方法,可以被继承 返回的是一个外部环境的参数
- }
- Collection.prototype.isEmpty = function(){ //静态方法,不能被继承
- return this.size == 0;
- }
- //定义一个ArrayList类型,它“继承”Collection类型
- function ArrayList(){
- var m_elements = []; //私有成员,不能被继承
- m_elements = Array.apply(m_elements,arguments);
- //ArrayList类型继承Collection
- this.base = Collection;
- this.base.call(this,m_elements.length);
- this.add = function(){
- return m_elements.push.apply(m_elements,arguments);
- }
- this.toArray = function(){
- return m_elements;
- }
- }
- ArrayList.prototype.toString = function(){
- return this.toArray().toString();
- }
- //定义一个SortedList类型,它继承ArrayList类型
- function SortedList(){
- //SortedList类型继承ArrayList
- this.base = ArrayList;
- this.base.apply(this,arguments);
- this.sort = function(){
- var arr = this.toArray();
- arr.sort.apply(arr,arguments);
- }
- }
- //构造一个ArrayList
- var a = new ArrayList(1,2,3);
- dwn(a);
- dwn(a.size()); //a从Collection继承了size()方法
- dwn(a.isEmpty); //但是a没有继承到isEmpty方法
- //构造一个SortedList
- var b = new SortedList(3,1,2);
- b.add(4,0); //b 从ArrayList继承了add()方法
- dwn(b.toArray()); //b 从ArrayList继承了toArray()方法
- b.sort(); //b 自己实现的sort()方法
- dwn(b.toArray());
- dwn(b); //b没有继承ArrayList中重写的toString()方法
- dwn(b.size()); //b 从Collection继承了size()方法
- //构造继承法不能满足子类和父类都是子类实例的“类型”
- dwn("构造继承法不能满足子类和父类都是子类实例的'类型':" + (a instanceof Collection));
- dwn("构造继承法不能满足子类和父类都是子类实例的'类型':" + (b instanceof ArrayList));
- -->
- </script>
- </body>
- </html>
- <html>
- <head>
- <title>Example-21.14 构造继承法</title>
- </head>
- <body>
- <script>
- <!--
- function dwn(s){
- document.write(s + "<br/>");
- }
- //定义一个Collection类型
- function Collection(size){
- this.size = function(){ return size; }; //公有方法,可以被继承 返回的是一个外部环境的参数
- }
- Collection.prototype.isEmpty = function(){ //静态方法,不能被继承
- return this.size == 0;
- }
- //定义一个ArrayList类型,它“继承”Collection类型
- function ArrayList(){
- var m_elements = []; //私有成员,不能被继承
- m_elements = Array.apply(m_elements,arguments);
- //ArrayList类型继承Collection
- this.base = Collection;
- this.base.call(this,m_elements.length);
- this.add = function(){
- return m_elements.push.apply(m_elements,arguments);
- }
- this.toArray = function(){
- return m_elements;
- }
- this.size = function(){ return m_elements.length; }
- }
- ArrayList.prototype.toString = function(){
- return this.toArray().toString();
- }
- //定义一个SortedList类型,它继承ArrayList类型
- function SortedList(){
- //SortedList类型继承ArrayList
- this.base = ArrayList;
- this.base.apply(this,arguments);
- this.sort = function(){
- var arr = this.toArray();
- arr.sort.apply(arr,arguments);
- }
- }
- //构造一个ArrayList
- var a = new ArrayList(1,2,3);
- dwn(a);
- dwn(a.size()); //a从Collection继承了size()方法
- dwn(a.isEmpty); //但是a没有继承到isEmpty方法
- //构造一个SortedList
- var b = new SortedList(3,1,2);
- b.add(4,0); //b 从ArrayList继承了add()方法
- dwn(b.toArray()); //b 从ArrayList继承了toArray()方法
- b.sort(); //b 自己实现的sort()方法
- dwn(b.toArray());
- dwn(b); //b没有继承ArrayList中重写的toString()方法
- dwn(b.size()); //b 从Collection继承了size()方法
- //构造继承法不能满足子类和父类都是子类实例的“类型”
- dwn("构造继承法不能满足子类和父类都是子类实例的'类型':" + (a instanceof Collection));
- dwn("构造继承法不能满足子类和父类都是子类实例的'类型':" + (b instanceof ArrayList));
- -->
- </script>
- </body>
- </html>
- this.size = function(){ return m_elements.length; }
例21.15 原型继承法.html
- <html>
- <head>
- <title>Example-21.15 原型继承法</title>
- </head>
- <body>
- <script>
- <!--
- function dwn(s){
- document.write(s + "<br/>");
- }
- //定义一个Point类型
- function Point(dimension){
- this.dimension = dimension;
- }
- //定义一个Point2D类型
- function Point2D(x,y){
- this.x = x;
- this.y = y;
- }
- Point2D.prototype.distance = function(){
- return Math.sqrt(this.x * this.x + this.y * this.y);
- }
- Point2D.prototype = new Point(2); //Point2D继承了Point
- //定义一个Point3D类型,也继承Point类型
- function Point3D(x,y,z){
- this.x = x;
- this.y = y;
- this.z = z;
- }
- Point3D.prototype = new Point(3); //Point3D也继承了Point
- //构造一个Point2D对象
- var p1 = new Point2D(0,0);
- //构造一个Point3D对象
- var p2 = new Point3D(0,1,2);
- dwn(p1.dimension);
- dwn(p2.dimension);
- dwn(p1 instanceof Point2D); //p1是一个Point2D
- dwn(p1 instanceof Point); //p1 也是一个Point
- dwn(p2 instanceof Point); //p2是一个Point
- -->
- </script>
- </body>
- </html>
运行结果:
2
3
true
true
true
类型的原型可以构成一个原型链,这样就能实现多个层次的继承,继承链上的每一个对象都是实例的类型。
例21.16 prototype的多重继承.html
- <script>
- function dwn(s){
- document.write(s + "<br/>");
- }
- function Point(){
- this.pointName = "Point";
- }
- //Point继承Object,这个通常可以省略,因为自定义类型的缺省原型为Object
- Point.prototype = new Object();
- function Point2D(){
- this.point2DName = "Point2D";
- }
- //Point2D继承Point
- Point2D.prototype = new Point();
- function ColorPoint2D(){
- this.colorPoint2DName = "ColorPoint2D";
- }
- //ColorPoint2D又继承Point2D
- ColorPoint2D.prototype = new Point2D();
- var colorPoint2D = new ColorPoint2D();
- dwn("colorPoint2DName:" + colorPoint2D.colorPoint2DName);
- dwn("point2DName:" + colorPoint2D.point2DName);
- dwn("pointName:" + colorPoint2D.pointName);
- </script>
运行结果:
colorPoint2DName:ColorPoint2D
point2DName:Point2D
pointName:Point
同构造继承法相比,原型继承法的优点是结构更加简单,而且不需要每次构造都调用父类的构造函数(尽管你仍然可以调用它),并且不需要通过复制属性的方式就能快速实现继承。但是它的缺点也是很明显的,首先它不方便直接支持多重继承,因为一个类型只能有一个原型;其次它不能很好地支持多参数和动态参数的父类构造,因为在原型继承的阶段你还不能决定以什么参数来实例化父类对象;第三是你被迫要在原型声明阶段实例化一个父类对象作为当前类型的原型,有的时候父类对象是不能也不应该随便实例化的;最后一个缺点是之前提到过的prototype的"副作用"。
同类继承相比,原型继承本来就是一个简化了的版本,因此我们不应该要求它完全达到标准的类继承的效果,实际上,当你的父类是一个简单、抽象的模型或者是一个接口的时候,原型继承的表现在已知的JavaScript对象继承中是最好的,甚至可以说,prototype继承才是JavaScript文法上提供的真正意义上的继承机制。所以,我们在使用JavaScript时,能够采用原型继承的地方,应当尽可能地采用这种继承方式。
面向对象不是只有类模型一种,prototype-based(基于原型)是class-based(基于类)的简化版本,是一种class-less的面向对象。对应地,prototype继承是class继承的简化版本,相对于class继承来说它简化了许多东西,例如省略了多重继承、基类构造函数、忽略了引用属性的继承......但不能因为它不支持这些特性,就不承认它是一种完整的继承,否则我们就在用class-based的眼光来看待prototype-based,实际上这可能是错误的。
其实prototype-based本来就是class-based的简化版,因此给继承加一个限制,要求父类必须是一个抽象类或者接口,那么prototype-based就没有任何问题了。当然了,也许这么做会使OOP的reuse(重用)能力减弱(以class-based的眼光来看),但是这可以通过其他机制来弥补,比如结合其他类型的继承方式,再比如闭包。
是否为继承添加额外的特性,开发者可以自由选择,但是在不需要这些额外特性的时候,还是有理由尽量用prototype-based继承。
总而言之,prototype-based认为语言本身可能不需要过分多的reuse能力,它牺牲了一些特性来保持语言的简洁,这没有错,prototype-based虽然比class-based简单,但它依然是真正意义上的object-oriented。
例21.17构造继承的局限性.html
- <html>
- <head>
- <title>例21.17构造继承的局限性</title>
- </head>
- <body>
- <script>
- <!--
- function MyDate(){
- this.base = Date;
- this.base.apply(this,arguments);
- }
- var date = new MyDate();
- alert(date.toGMTString);
- //核心对象的某些方法不能被构造继承,原因是核心对象并不像我们自定义的一般对象那样
- //在构造函数里进行赋值或初始化操作
- -->
- </script>
- </body>
- </html>
例21.18 原型继承的局限性.html
- <html>
- <head>
- <title>例21.18 原型继承的局限性</title>
- </head>
- <body>
- <script>
- <!--
- function MyDate(){}
- MyDate.prototype = new Date();
- var date = new MyDate();
- alert(date.toGMTString);
- date.toGMTString();//Date.prototype.toUTCString called on incompatible Object
- -->
- </script>
- </body>
- </html>
我的想法:核心对象Date根本就是用C语言或者其他语言实现的,js本身的继承怎么能够很好的继承呢?
构造函数通常没有返回值,它们只是初始化由this值传递进来的对象,并且什么也不返回。如果函数有返回值,被返回的对象就成了new表达式的值。
111