(1)第一种是以 原型链的方式 来实现继承。
// 原型链继承
function SuperType(){
this.property = true
}
SuperType.prototype.getSupertypeValue = function (){
return this.property
}
function SubType(){
this.suberProperty = false
}
SubType.prototype = new SuperType()
SubType.prototype.getSubValue = function (){
return this.subProperty
}
let instance = new SubType()
console.log(instance.getSupertypeValue())
console.log(instance.constructor)
但是这种实现方式存在的缺点是,在包含有引用类型的数据时,会被所有的实例对象所共享, 容易造成修改的混乱;
还有就是在创建子类型的时候不能向超类型传递参数,实际上,应该说是没有办法在不影响所有对象实例的情况下,给超类型的构造函数传递参数。
注意:instance.constructor现在指向的是SuperType,因为SubType的原型指向了另一个对象——SuperType的原型,而这个对象的constructor属性指向的是SuperType。
function SuperType2(){
this.colors = ["red","yellow"]
}
function SuberType2() {
}
SuberType2.prototype = new SuperType2()
let instance1 = new SuberType2()
instance1.colors.push("blue")
console.log(instance1.colors)
let instance2 = new SuberType2()
console.log(instance2.colors)
(2)第二种方式是使用借用构造函数的方式,这种方式是通过在子类型的函数中调用超类型的构造函数来实现的。
// 构造函数
function SuperType3() {
this.colors = ["green","black"]
}
function SuberType3() {
SuperType3.call(this)
}
let instance3 = new SuberType3()
instance3.colors.push("white")
console.log(instance3.colors)
let instances4 = new SuberType3()
console.log(instances4.colors)
这一种方法解决了不能向超类型传递参数的缺点,但是它存在的一个问题就是无法实现函数方法的复用,并且超类型原型定义的方法子类型也没有办法访问到。
// 构造函数
function SuperType33(name) {
this.name = name
}
function SuberType33() {
SuperType3.call(this,"samm")
this.age = "22"
}
let instance33 = new SuberType33()
console.log(instance33.name)
console.log(instance33.age)
(3)第三种方式是组合继承,组合继承是将原型链和借用构造函数 组合起来使用的一种方式。
通过借用构造函数的方式来实现类型的属性的继承,通过将子类型的原型设置为超类型的实例来实现方法的继承。
// 组合继承
function SuperType4(name) {
this.name = name
this.colors = ["red","blue"]
}
SuperType4.prototype.sayName = function () {
console.log(this.name)
}
function SuberType4(name,age) {
//继承属性
SuperType4.call(this,name)
this.age = age
}
SuberType4.prototype = new SuperType4()
SuberType.prototype.constructor = SuberType4
SuberType4.prototype.sayAge = function () {
console.log(this.age)
}
let instance5 = new SuberType4("jessica",29)
instance5.colors.push("black")
console.log(instance5.colors)
instance5.sayName()
instance5.sayAge()
let instance6 = new SuberType4("samm","27")
console.log(instance6.colors)
instance6.sayName()
instance6.sayAge()
这种方式解决了上面的两种模式单独使用时的问题。
但是由于我们是以超类型的实例来作为子类型的原型,所以调用了两次超类的构造函数,造成了子类型的原型中多了很多不必要的属性。
(4)第四种方式是原型式继承,原型式继承的主要思路就是基于已有的对象来创建新的对象,实现的原理是,向函数中传入一个对象, 然后返回一个以这个对象为原型的对象。这种继承的思路主要不是为了实现创造一种新的类型,只是对某个对象实现一种简单继承,ES5 中定义的 Object.create() 方法就是原型式继承的实现。缺点与原型链方式相同。
//原型式继承
function object(o){
function F(){}
F.prototype = o
return new F()
}
const person = {
name:"jessica",
friends:["Shelby","Court","Van"]
}
const anotherPerson = object(person)
anotherPerson.name = "Greg"
anotherPerson.friends.push("Rob")
console.log(anotherPerson)
console.log(anotherPerson.friends)
const yetAnotherPerson = object(person)
yetAnotherPerson.name = "Linda"
yetAnotherPerson.friends.push("Barbie")
console.log(yetAnotherPerson)
console.log(yetAnotherPerson.friends)
console.log(person)
(5)第五种方式是寄生式继承,寄生式继承的思路是创建一个用于封装继承过程的函数,通过传入一个对象,然后复制一个对象的副本, 然后对象进行扩展,最后返回这个对象。这个扩展的过程就可以理解是一种继承。这种继承的优点就是对一个简单对象实现继承,如果这个对象不是自定义类型时。缺点是没有办法实现函数的复用。
//寄生式继承
function createAnother(original){
let clone = object(original) //object,原型式继承有定义
clone.sayHi = function () {
console.log("hi")
}
return clone
}
const person = {
name: "Nicholas",
friends:["Shelby","Court","Van"]
}
let anotherPerson = createAnother(person)
anotherPerson.sayHi()
(6)第六种方式是寄生式组合继承,组合继承的缺点就是使用超类型的实例做为子类型的原型,导致添加了不必要的原型属性。寄生式组合继承的方式是使用超类型的原型的副本来作为子类型的原型,这样就避免了创建不必要的属性。
//寄生组合式继承
function inheritPrototype(subType,superType){
let prototype = Object(superType.prototype)
prototype.constructor = subType
subType.prototype = prototype
}
function SuperType(name){
this.name = name
this.colors = ["red","blue"]
}
SuperType.prototype.sayName = function () {
console.log(this.name)
}
function SubType(name, age){
SuperType.call(this,name)
this.age = age
}
inheritPrototype(SubType,SuperType)
SubType.prototype.sayAge = function (){
console.log(this.age)
}