JS中继承方式

  1. 原型链继承
  2. 借用构造继承
  3. 组合继承
  4. 原型式继承
  5. 寄生式继承
  6. 寄生组合式继承

一 、原型链继承

本质:通过将一个构造函数的实例赋值给另一个构造函数的原型,实现是重写原型对象,代之以一个新类型的实例。

function SuperType(){ 
    this.property = true; // 实例属性
} 
SuperType.prototype.getSuperValue = function(){   //原型方法
    return this.property; 
}; 
function SubType(){ 
    this.subproperty = false; 
} 
//继承了SuperType ,替换原型语句
SubType.prototype = new SuperType(); 
SubType.prototype.getSubValue = function (){ 
    return this.subproperty; 
}; 
var instance = new SubType(); 
alert(instance.getSuperValue());      //true 

这个例子中SubType的原型被新原型SuperType实例替代了,原来存在于SuperType实例中所有属性和方法现在也存在于SubType.prototype中,在确立继承后,又给SubType添加一个新的方法,这样就在继承了SuperType实例中所有属性和方法之上又添加了一个新的方法。

最终结果instance指向SubType的原型,SubType原型又指向SuperType原型。getSuperValue方法任然还在SuperType.prototype中,但是property则位于SubType.prototype中。因为property属性是一个实例属性,而getSuperValue则是一个原型方法,既然SubType.prototype现在是SuperType的实例,那么property当然就位于该实例中。

注意:

  1. 所有引用类型都继承于Object,所有函数的默认原型都是Object实例,默认的原型链中包含一个内部指针,指向Object.prototype。
  2. 给原型SubType添加方法一定要放在替换原型语句之后。
  3. 通过原型链实现继承时,不能通过对象字面量创建原型方法,这样会重写原型链。例如
function SuperType(){ 
    this.property = true; 
} 
SuperType.prototype.getSuperValue = function(){ 
    return this.property; 
}; 
function SubType(){ 
    this.subproperty = false; 
} 
//继承了SuperType 
SubType.prototype = new SuperType(); 
//使用字面量添加新方法,会导致上一行代码无效
SubType.prototype = { 
    getSubValue : function (){ 
        return this.subproperty; 
    }, 
    someOtherMethod : function (){ 
        return false; 
    } 
}; 
var instance = new SubType(); 
alert(instance.getSuperValue());   //error! 

存在的问题:

  1. 所有实例共享同一个包含引用类型值的原型属性,一个实例修改了属性,另一个实例中获得的属性也会被修改;
  2. 创建子类型实例时,不能向超类型中传递参数;

确定实例和原型的关系:

1)instanceof操作符

alert(instance instanceof Object); //true

alert(instance instanceof SuperType); //true

alert(instance instanceof SubType); //true

2)isPrototypeOf()方法

alert(Object.prototype.isPrototypeOf(instance)); //true

alert(SuperType.prototype.isPrototypeOf(instance)); //true

alert(SubType.prototype.isPrototypeOf(instance)); //true

二、借用构造继承

本质:在子类型构造函数内部调用超类型构造函数——call()和apply()

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 

通过使用call()和apply()实际在每次新创建的SubType实例的环境中都重新调用了SuperType构造函数,这样就会在新SubType对象上执行SuperType函数中定义的所有对象初始化代码。结果SubType的每个实例就会具有自己的属性的副本了。

存在问题:方法都在构造函数中定义,因此函数复用就无从谈起了。

三、组合继承

本质:使用原型链实现对原型方法的继承,而通过借用构造函数来实现对原型属性的继承。

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); 
    this.age = age; 
} 
//继承方法
SubType.prototype = new SuperType(); 
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 

第一步:Subtype构造函数在调用SuperType构造函数时传入name参数,又定义了自己的属性age。

第二步:将SuperType实例赋值给Subtype原型,又定义了自己的方法sayAge

这样使得Subtype不同的实例拥有原型属性的新副本,又共享同一个原型方法。

缺点:调用两次超类型的构造函数

四、原型式继承

本质:要求必须有一个对象作为另一个对象的基础,然后将这个基础对象传入object()函数做了一个浅复制,然后再根据具体需求对得到的对象加以修改(对象的继承)

使用场景:在没有必要创建构造函数,只想让一个对象与另一个对象保持类似的情况下可以使用。

object()函数如下,在函数内部,先创建一个临时性的构造函数,然后传入的对象作为这个构造函数的原型,最后返回这个构造函数的新实例。

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" 

ES5中Object.create()方法规范化原型式继承,接受两个参数:一个是基础对象,一个是为新对象定义额外属性的对象(可选)。当只有一个参数时,和上面定义的Object函数作用相同;如果有第二个参数,这个对象是通过自己的描述符定义的,以这种方式指定的任何属性都会覆盖原型对象上的属性。

var person = { 
    name: "Nicholas", 
    friends: ["Shelby", "Court", "Van"] 
}; 
var anotherPerson = Object.create(person, { 
name: { 
value: "Greg" 
} 
})
; 
alert(anotherPerson.name); //"Greg" 

缺点:内存占用率高,因为拷贝了父类型的属性

五、寄生式继承

本质:创建一个仅用于封装继承过程的函数,该函数内部已某种方式来增强对象,最后再返回对象。(在原生式继承基础上做了封装)

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" 

createAnother函数接受一个参数,也就是作为新对象基础的对象,然后将这个对象传递给object函数,将返回的结果赋值给clone,再给clone对象添加新的方法,最后返回对象。

适用场景:在主要考虑对象而不是自定义类型和构造函数情况下适用。任何能够返回新对象的函数都适用此模式。

缺点:实例是父类的实例,不是子类的实例

六、寄生组合式继承

由于组合继承无论在什么情况下,都会调用两次超类型构造函数,如下。为了解决这个问题提出来寄生组合继承

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);         //第二次调用
SuperType() 
    this.age = age; 
} 
SubType.prototype = new SuperType();    //第一次调用
SuperType() 
SubType.prototype.constructor = SubType; 
SubType.prototype.sayAge = function(){ 
    alert(this.age); 
}; 

本质:通过借用构造函数来继承属性,通过原型链的混成形式来继承方法。即使用寄生式继承来继承超类型的原型,然后将结果指定给子类型的原型。                                                                                                                                                                  

function inheritPrototype(subType, superType){ 
    var prototype = object(superType.prototype);     //创建对象
    prototype.constructor = subType;                 //增强对象
    subType.prototype = prototype;                   //指定对象
} 

这个函数接受两个参数:一个是子类型构造函数,一个是超类型构造函数。在函数内部,第一步创建超类型原型的一个副本,第二步为创建的副本添加constructor属性,从而弥补因重写原型而丢失的默认的constructor属性,最后一步将创建的对象复制给子类型的原型。实例:

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); 
    this.age = age; 
} 
inheritPrototype(SubType, SuperType); 
SubType.prototype.sayAge = function(){ 
    alert(this.age); 
}; 

猜你喜欢

转载自blog.csdn.net/Rnger/article/details/82084209
今日推荐