1 <script type="text/javascript">
2 //1、继承
3 //2、原型链
4 //3、借用构造函数
5 //4、组合继承
6 //5、原型式继承
7 //6、寄生式继承
8 //7、寄生组合式继承
9
10 //1、继承是OO中最让人津津乐道的概念。许多OO语言都支持两种继承方式:接口继承和实现继承。
11 //接口继承只支持方法签名,而实现继承则继承实际的方法。
12 //由于函数没有签名,所以在ECMAScript中无法实现接口继承,只支持实现继承,而且其实现继承主要由原型链来实现。
13
14 //2、原型链。
15 //简单回顾一下,构造函数、原型和实例的关系:每个构造函数都有一个原型对象,原型对象都包含一个指向构造函数的指针
16 //而实例都包含一个指向原型对象的内部指针。
17 //而如果我们让原型对象指向另一个类型的实例,结果会怎样呢?显然,此时的原型对象将包含一个指向另一个原型的指针,
18 //相应的,另一个原型中也包含着一个指向另一个构造函数的指针。假如另一个原型又是另一个类型的实例,那么上述关系依然成立,
19 //就构成了实例与原型的链条。这就是所谓原型链的基本概念。eg:
20
21 //动物类
22 function Animal() {
23 this.shout = "不知道";
24 }
25 Animal.prototype.getAnimalValue = function () {
26 return this.shout;
27 }
28
29 //狗狗类
30 function Dog() {
31 this.dogShout = "汪汪汪~";
32 }
33
34 ///狗狗类继承动物类
35 Dog.prototype = new Animal();
36
37 Dog.prototype.getDogValue = function () {
38 return this.dogShout;
39 }
40
41 var instance = new Dog();
42 //狗狗类调用动物类叫的方法
43 alert(instance.getAnimalValue())
44 //instance.getAnimalValue()会经历3个步骤,1、搜索实例 2、搜索Dog.prototype 3、搜索Animal.prototype
45 //a、别忘记默认的原型。大家要记住,所有的函数的默认原型都是Object的实例,因此默认原型都会有一个内部指针,
46 //指向Object.prototype。这也正是自定义类都会继承toString()和valueOf()的方法的根本原因
47
48
49 //b、确定原型和实例的关系
50 alert(instance instanceof Object);//true
51 alert(instance instanceof Animal);//true
52 alert(instance instanceof Dog);//true
53
54
55 alert(Object.prototype.isPrototypeOf(instance));//true
56 alert(Animal.prototype.isPrototypeOf(instance));//true
57 alert(Dog.prototype.isPrototypeOf(instance));//true
58
59 //c、原型链的问题
60 //想必大家还记得,前面介绍过含引用类型值的原型会被所有的实例共享,
61 //这也是为什么要在构造函数中,而不是在原型对象中定义属性的原因。
62 //所以,实践中很少有人单独使用原型链。
63
64
65 //3、借用构造函数(又叫:伪造对象或经典继承)
66 //这种继承的方式很简单,就是在子类构造函数内部调用超类构造函数使用call或apply
67 function Animal() {
68 this.colors = ['red', 'blue', 'green'];
69 }
70
71 function Dog() {
72 //继承了Animal类
73 Animal.call(this);
74 }
75
76 var dog = new Dog();
77 dog.colors.push("black");
78 alert(dog.colors);//'red','blue','green','black'
79
80 var dog2 = new Dog();
81 alert(dog2.colors);//'red','blue','green'
82
83 //相对于原型链而言,借用构造函数有很大的优势,即可以在子类构造函数中向超类传递参数
84 function Animal2(name) {
85 this.name = name;
86 }
87
88 function Dog2() {
89 //继承了Animal类
90 Animal2.call(this, "大黄");
91 this.age = 2;
92 }
93
94 var dog = new Dog2();
95 alert("年龄:" + dog.age + ",名称:" + dog.name);
96
97 //因为方法都在构造函数中定义,因此函数复用就无从谈起了。而且在超类原型中定义的方法,对于子类型而言也是不可见的
98 //结果所有类型都只能使用构造函数模式。
99 //考虑到这些问题,借用构造函数的技术也很少单独使用。
100
101 //4、组合继承
102 function Animal3(name) {
103 this.name = name;
104 this.colors = ['red', 'blue', 'green'];
105 }
106
107 Animal3.prototype.sayName = function () {
108 alert(this.name)
109 }
110
111 function Dog3(name, age) {
112 //继承属性
113 Animal3.call(this, name);
114 this.age = age;
115 }
116 //继承方法
117 Dog3.prototype = new Animal3();
118 Dog3.prototype.constructor = Dog3;
119 Dog3.prototype.sayAge = function () {
120 alert(this.age)
121 }
122
123 var d3 = new Dog3("小黄", 1);
124 d3.colors.push("black");
125 alert(d3.colors);//red,blue,green,black
126 d3.sayAge();//1
127 d3.sayName();//小黄
128
129 //组合继承避免了原型链和借用构造函数的缺陷,融合了他们的优点,成为javascript中最常用继承方式
130 //而且instanceof 和 isPrototypeOf也能够用于识别基于组合继承创建的对象。
131
132 //5、原型式继承
133 //在没有必要兴师动众的创建构造函数,而只想让一个对象与另一个对象保持类似的情况下,原型式继承是完全可以胜任的
134 //不过别忘了:包含引用类型的值的属性始终都会共享相应的值,就像使用原型模式一样。
135
136 var Person={
137 name:'xulei',
138 friends:['张三','李四','王五']
139 }
140
141 var p1=Object.create(Person);
142 p1.name="徐磊1";
143 p1.friends.push("killey");
144 var p2=Object.create(Person);
145 p2.name="徐磊2";
146 p2.friends.push("jack");
147
148
149 alert("p1.name:"+p1.name);//徐磊1
150 alert("p1.friends:"+p1.friends);//张三','李四','王五','killey','jack'
151 alert("p2.name:"+p2.name);//徐磊2
152 alert("p2.friends:"+p2.friends);//张三','李四','王五','killey','jack'
153
154
155 //6、寄生式继承
156 //寄生式继承是与原型式继承紧密相关的一种思路。寄生式继承的思路与寄生构造函数和工厂模式类似,即创建一个仅用于封装继承过程的函数
157 //该函数在内部以某种方式来增强对象,最后再像真的是它做了所有工作一样返回对象。eg:
158 function object(o) {
159 function F() { }
160 F.prototype=o;
161 return new F();
162 }
163 function createAnother(original){
164 var clone=object(original);//通过调用函数来创建一个新对象
165 clone.sayHi=function(){//以某种方式来增强这个对象
166 alert("hi");
167 };
168 return clone;//返回这个对象
169 }
170 var Person2={
171 name:'xulei',
172 friends:['张三','李四','王五']
173 }
174
175 var p3=createAnother(Person2);
176 p3.sayHi();
177
178
179 //7、寄生组合式继承
180 //前面说过,组合继承是最常用继承模式,不过它也有自己的不足。
181 //组合继承最大的问题就是无论在什么情况下,都会调用两次超类型的构造函数:一次是在创建子类型原型的时候,另一次是在子类型构造函数内部
182 //eg:
183 function SuperType(name){
184 this.name=name;
185 this.colors=['red','blue','green'];
186 }
187
188 SuperType.prototype.sayName=function(){
189 alert(this.name);
190 }
191
192 function SubType(name,age) {
193 SuperType.call(this,name);//第二次调用SuperType
194 this.age=age;
195 }
196
197 SubType.prototype=new SuperType();//第一次调用SuperType
198 SubType.prototype.constructor=SubType;
199 SubType.prototype.sayAge=function(){
200 alert(this.age);
201 }
202
203 //好在已经有更好的解决方案————寄生组合式继承
204 //所谓寄生组合式继承就是借用构造函数来继承属性,通过原型链的混成形式来继承方法。
205 //其背后的基本思路是:不必为了指定子类型的原型而调用超类型的构造函数,我们所需要的无非是超类型原型的一个副本而已
206 //本质上,就是使用寄生继承来继承超类型的原型,然后再将结果指定给子类型的原型。eg:
207 function object(o) {
208 function F() { }
209 F.prototype=o;
210 return new F();
211 }
212 function inheritPrototype(subType,superType) {
213 var prototype=object(superType.prototype);//创建对象
214 prototype.constructor=subType;//增强对象
215 subType.prototype=prototype;//指定对象
216 }
217
218 function SuperType(name){
219 this.name=name;
220 this.colors=['red','blue','green'];
221 }
222
223 SuperType.prototype.sayName=function(){
224 alert(this.name);
225 }
226
227 function SubType(name,age) {
228 SuperType.call(this,name);
229 this.age=age;
230 }
231 inheritPrototype(SubType,SuperType);
232 SubType.prototype.sayAge=function(){
233 alert(this.age);
234 }
235
236 var p4=new SubType("张三",29);
237 p4.sayName();
238 //这个例子的高效率体现在它只调用了一次SuperType构造函数,并且因此避免了在SubType.prototype上创建不必要的、多余的属性
239 //与此同时,原型链还能保持不变;因此还能正常的使用instanceOf 和isPrototypeOf
240 //普遍认为寄生组合继承是引用类型最理想的继承方式。
241
242
243
244
245
246 </script>
javascript——面向对象程序设计(4)
最新推荐文章于 2024-07-20 10:36:53 发布