javascript——面向对象程序设计(4)
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>
如果觉得对你有所帮助就打点一下吧