JavaScrip继承图文总结

JavaScript有多种继承模式,总结起来用到的方法有:原型链的传递、构造函数的借用、对象的复制。

 
 


这篇文章讲得很清晰,让我们明白:所有JS对象源于null,并通过原型指针和原型对象来实现继承。

构造函数和原型对象的关系如下:


 
 

每个构造函数都有一个prototype属性,指向函数的原型对象;原型对象中又有一个constructor属性,重新指向构造函数。而对象实例中有一个原型指针[[prototype]](在Firefox、Safari和Chrome中,对应属性proto),指向原型对象。
明白原型之后,就进入正题继承了。

1.原型链继承

function SuperType(){
    this.superproperty = true; } SuperType.prototype.getSuperValue = function(){ return this.superproperty; } function SubType(){ this.subproperty = false; } SubType.prototype = new SuperType(); //原型链继承 SubType.prototype.getSubValue =function(){ return this.subproperty; } var instance = new SubType(); alert(instance.getSuperValue());//true 
 
 

通过SubType.prototype=new SuperType(),实现了以下三点:
A:重写了SubType的原型,让子类原型和子类构造函数之间断开联系。
B:子类原型是父类的实例,其原型指针[[prototype]]指向了父类的原型对象,这样子类就可以沿着原型链访问到父类的方法getSuperValue()。
C:子类原型是父类实例,通过父类构造函数,子类原型继承了父类的属性superproperty。
最终,子类继承了父类的方法和属性。
原型链继承的问题:
父类的实例属性成了子类的原型属性,如上面的superproperty,会被子类所有实例共享。该属性是基本类型值时没有问题,但如果是引用类型值(比如数组),那么修改实例1的该属性(比如向数组push一个新值),实例2也会跟着改变。
也就是说,实例们只有共性,不能保持个性。

2.构造函数继承

function SuperType(name){ 
    this.name = name; } function SubType(){ //继承了SuperType,同时还传递了参数 SuperType.call(this,"Nicholas"); this.age = 29; } var instance = new SubType(); alert(instance.name); //"Nicholas" alert(instance.age); //29 
 
 

通过SuperType.call(this),在子类构造函数中调用了父类的构造函数,创建子类实例会执行子类的构造函数(含父类的构造函数),也就完成了继承。当然,子类和父类的原型是没有关系的,子类实例不能访问父类原型对象中的属性和方法。
构造函数继承的问题:
方法在构造函数中定义,无法实现函数复用。比如父类中有一个方法getName(),则每次创建子类实例的时候,都要创建一个新的getName(),通过instance1.getName() !== instance.getName()就可以验证这一点。
这就是说,实例们保持了个性,却不能共享方法。

3.组合继承

function SuperType(name){
     this.name = name; this.colors = ["red","blue","green"]; } SuperType.prototype.sayName = function(){ alert(this.name); } function SubType(name,age){ //继承属性 SuperType.call(this,name); //第二次调用SuperTyper() this.age = age; } //继承方法 SubType.prototype = new SuperType(); //第一次调用SuperTyper() SubType.prototype.constructor = SubType; SubType.prototype.sayAge = function(){ alert(this.age); } var instance1 = new SubType("Nicholas",29); instance1.colors.push("black"); alert(instance1.colors); // "red","blue","green","black" instance1.sayName(); //"Nicholas" instance1.sayAge(); //29 var instance2 = new SubType("Greg",27); alert(instance2.colors); // "red","blue","green" instance2.sayName(); //"Greg" instance2.sayAge(); //27 
 
 

通过借用构造函数来继承属性,原型链来继承方法。结合了两者的优点,让实例们即保持个性,又共享方法。
组合继承的问题:
两次调用父类的构造函数。第一次(A):SubType.prototype=new SuperType(),子类原型对象取得了父类的实例属性。第二次(B):
SuperType.call(this),创建子类实例时,调用父类构造函数,重写实例属性,屏蔽了原型对象上同名属性。

4.原型式继承

function object(o){ 
    function F(){}; F.prototype = o; return new F(); } var person = { name:"Nicholas", friends:["Shelby","Court","Van"] }; var anotherPerson = object(person); anotherPerson.name = "Greg"; anotherPerson.friends.push("Rob"); var yetAnotherPerson = object(person); yetAnotherPerson.name = "Linda"; yetAnotherPerson.friends.push("Barbie"); alert(person.friends); //"Shelby,Court,Van,Rob,Barbie" 
 
 

以一个对象为基础,通过object()函数进行浅复制,再将得到的对象实例加以修改。
可以看到,这种继承方法是没有父类和子类的,只是通过复制对象来得到副本。
ES5有object.create()方法,是object()的规范化,可以传入两个参数:要复制的对象和额外的属性对象(如{name:{value:Greg}},这种方式会覆盖基础对象上的同名属性)。
原型式继承的问题:
和原型链继承一样,继承的属性由所有实例共享,改动一个实例的引用类型值时,所有实例都会改变。

5.寄生式继承

function createAnother(original){
    var clone = object(original);//object()函数创建对象 clone.sayHi = function(){ //增强这个对象 alert("hi"); }; return clone; //返回这个对象 } var person = { name:"Nicholas"; friends:["Shelby","Court","Van"]; } //基础对象 var anotherPerson = createAnother(person); //新对象 anotherPerson.sayHi(); //"hi" 

寄生式和原型式方法相同,都是复制一个基础对象来得到新对象,不同的是它将对象实例的修改放到也放到函数中,将整个过程(创建、增强、返回)封装了起来。

6.寄生组合式继承

function inheritPrototype(subType,superType){
    var prototype = object(superType.prototype); //创建对象 prototype.constructor = subType; //增强对象 subType.prototype = prototype; //指定对象 } function SuperType(name){ this.name = name; this.colors = ["red","blue","green"]; } SuperType.prototype.sayName = function(){ alert(this.name); } function SubType(name,age){ //继承属性 SuperType.call(this,name); //只调用一次SuperTyper() this.age = age; } //继承方法 inheritPrototype(SubType,SuperType); SubType.prototype.sayAge = function(){ alert(this.age); } 
 
 

顾名思义,这种继承模式就是寄生式(复制)+组合式(原型链+构造函数),将几种方法组合起来。解决了组合式继承两次调用父类构造函数的问题。

扫描二维码关注公众号,回复: 1737650 查看本文章

猜你喜欢

转载自www.cnblogs.com/littlewrong/p/9221166.html