JavaScript中的继承
JavaScript中的继承是通过原型链实现的,有几种设计继承的方法总结如下:
一. 原型链
基本思想:每个构造函数都有一个原型对象,原型对象又都包含一个指向构造函数的指针,而实例又指向一个原型对象的指针,那么,如果我们让原型对象等于另一个类型的实例,此时的原型对象将包含一个指向另一个原型的指针,相应的另一个原型中也包含着一个指向另一个构造函数的指针,假如另一个原型又是另一个类型的实例呢,那么上述的关系依然成立,如此层层递进,就构成了实例和原型的链条。下面是例子
1 function SuperType(){ 2 this.property = true; 3 } 4 SuperType.prototype.getSuperValue = function(){ 5 return this.property; 6 } 7 function SubType(){ 8 this.subproperty = false; 9 } 10 //继承SuperType 11 SubType.prototype = new SuperType(); 12 SubType.prototype.getSubValue = function(){ 13 return this.subproperty; 14 }; 15 var instance = new SubType(); 16 alert(instace.getSuperValue());//true
缺点:引用类型值的属性会被所有实例共享,假设有两个子类型的实例,其中一个实例修改了从超类型继承而来的引用类型的值,修改的结果也同样会在另一个实例中显示出来;
创建子类型的实例的时候,不能向超类型的构造函数中传递参数。
二.借用构造函数
基本思想:在子类型构造函数的内部调用超类型构造函数,即通过apply和call方法来调用构造函数
1 function SuperType(){ 2 this.colors = ["red","blue","green"]; 3 } 4 function SubType(){ 5 //继承了SuperType 6 SuperType.call(this); 7 } 8 var instance1 = new SubType(); 9 instance1.colors.push("black"); 10 alert(instance1.colors);//"red,blue,green,black" 11 var instance2 = new SubType(); 12 alert(instance2.colors);//"red,blue,green"
优点:解决了原型中包含引用类型值所带来的问题
缺点:超类型的原型中定义的方法,对子类型而言也是不可见的(没有指向超类型原型的指针,只有指向构造函数的),结果所有类型都只能使用构造函数模式,这样肯定是不好的。
三.组合继承
基本思想:使用原型链实现对原型属性和方法的继承,通过借用构造函数来实现对实例属性的继承下面是例子
1 function SuperType(name){ 2 this.name = name; 3 this.colors = ["red","blue","green"]; 4 } 5 SuperType.prototype.sayName = function(){ 6 alert(this.name); 7 }; 8 function SubType(name,age){ 9 //继承属性 10 SuperType.call(this,name); 11 this.age = age; 12 } 13 //继承方法 14 SubType.prototype = new SuperType(); 15 SubType.prototype.constructor = SubType; 16 SubType.prototype.sayAge = function(){ 17 alert(this.age); 18 }; 19 var instance1 = new SubType("Nicholas",29); 20 instance1.colors.push("black"); 21 alert(instance1.colors);//"red,blue,green,black" 22 instance1.sayName();//"Nicholas" 23 instance1.sayAge();//29 24 var instance2 = new SubType("Greg",27); 25 alert(instance2.colors);//"red,blue,green" 26 instance2.sayName();//"Greg" 27 instance2.sayAge();//27
组合继承是最常用的继承模式,而且,利用instanceof 和isprototypeof()也能够用于识别基于组合继承创建的对象
四.原型式继承
基本思想:借助原型可以基于已有的对象创建新对象,同时,不必因此创建自定义类型。下面是例子
1 function object(o){ 2 function F(){}; 3 F.prototype = o; 4 return new F(); 5 } 6 var person = { 7 name:"Nicholas", 8 friends:["shelby","Cour","Van"] 9 }; 10 var anotherPerson = object(person); 11 anotherPerson.name = "Greg"; 12 anotherPerson.friends.push("Rob"); 13 var yetAnotherPerson = object(person); 14 yetAnotherPerson.name = "Linda"; 15 yetAnotherPerson.friends.push("Barbie"); 16 alert(person.friends);//"Shelby,Court,Van,Rob,Barbie"
缺点:还是包含引用类型值得属性始终会共享相应的值,实例中修改会在原型中展现。
无论什么情况下都会调用两次超类型构造函数:一次是在创建子类型原型的时候,另一次是在子类型构造函数内部,子类型最后会包含超类型中的全部实例属性,但有时候我们会在调用子类型构造函数时重写这些属性。
五.寄生式继承
基本思路:与寄生构造函数和工厂模式类似,即创建一个仅用于封装继承过程的函数,该函数在内部以某种方式类增强对象。下面是例子:
function object(o){ function F(){}; F.prototype = o; return new F(); } function createAnother(original){ var clone = object(original);//通过调用函数来创建一个新对象 clone.sayHi = function(){ alert("hi"); }; return clone; } var person = { name:"Nicholas", friends:["Shelby","Court","Van"] }; var anotherPerson = createAnother(person); anotherPerson.sayHi();//"Hi"
使用场景:在主要考虑对象而不是自定义类型和构造函数的情况下。
六.寄生组合式继承
为了解决组合继承的缺点
基本思想:通过借用构造函数来继承属性,通过原型链的混成形式来继承方法
function object(o){ function F(){}; F.prototype = o; return new F(); } function inheritPrototype(subType,superType){ var prototype = object(superType.ptorotype);//创建对象 prototype.constructor = subType;//增强对象 subType.prototype = prototype;//指定对象 }
能够正常使用instanceof() 和 isPrototypeOf()。