【Javascript】常见继承

116 阅读3分钟

继承

继承是利用原型让一个引用类型继承另一个引用类型的属性和方法。 函数都有一个 prototype 属性,该属性指向一个对象(这个对象指的是函数的原型对象)。

原型链继承

Parent.prototype.getSuperValue = function(){
    return this.property
 }

function Parent(){
    this.property = true
 }
 
 function Child(){
    this.subproperty = false
}

Child.prototype = new Parent()

var p = new Child()
console.log(p.getSuperValue());  //true

var p2 = new Child()
console.log(p2);   //Parent { subproperty: false }

缺点:

  • 原型实际上会变成另一个类型的实例,并且这个实例中的属性会被所有的子类实例所共享。
function Parent () {
    this.names = ['kevin', 'daisy'];
}

function Child () {

}

Child.prototype = new Parent();

var child1 = new Child();

child1.names.push('LiHua');

console.log(child1.names); // ["kevin", "daisy", "LiHua"]

var child2 = new Child();

console.log(child2.names); // ["kevin", "daisy", "LiHua"]
  • 在创建子类型的实例时,做不到向父类型的构造函数传参。

构造函数继承(经典继承)

function Parent(name){
    this.colors = ['red', 'green', 'blue']
}

function Child(){
    Parent.call(this)
}
// Child.prototype = new Parent()

var p1 = new Child()
p1.colors.push('pink')
console.log(p1); //['red', 'green', 'blue', 'pink']
var p2 = new Child()
console.log(p2);//['red', 'green', 'blue']

优点:

  • 避免了引用类型的属性被所有实例共享
  • 可以在 Child 中向 Parent 传参
function Parent(name){
    this.name = name
}

function Child(name){
    Parent.call(this, name)
}
// Child.prototype = new Parent()

var p1 = new Child('Judy')
console.log(p1.name); //Judy
var p2 = new Child('Lisa')
console.log(p2.name);//Lisa

缺点:

  • 子类实例无法继承到父类构造函数原型上的属性和方法。
  • 所有的属性和方法都只能写在构造函数内部,降低了代码的复用性。

组合继承(伪经典继承)

Parent.prototype.sayname = function(){
    console.log(this.name);
 }

function Parent(name){
    this.name = name
    this.colors = ['red', 'green', 'blue']
 }

function Child(name, age){
    Parent.call(this,name)
    this.age = age
 }
Child.prototype = new Parent()
Child.prototype.constructor = Child
Child.prototype.sayAge = function(){
    console.log(this.age);
 }

var p1 = new Child('LiHua', 18)
p1.colors.push('yellow')
p1.sayname() //LiHua
p1.sayAge() //18
console.log(p1.colors) //[ 'red', 'green', 'blue', 'yellow' ]
var p2 = new Child('Lisa', 20)
p2.sayname() //Lisa
p2.sayAge() //20
console.log(p2.colors) //[ 'red', 'green', 'blue' ]

console.log(p1 pof Child);//true

缺点:

  • 超类构造函数无论何时,都要被调用两次

原型式继承

function object(o) {
     function F() {}
         F.prototype = o
         return new F()
 }

单纯的对象之间可以胜任,包含引用类型值的属性始终会共享

var person = {
    name:'Ming',
    colors:['red', 'green', 'blue']
}

var person1 = Object.create(person, {
    name: {
        value: 'LiHua'
    }
})

var person2 = Object.create(person, {
    name: {
        value: 'Lisa'
    }
})

person1.colors.push('yellow')
console.log(person1.name); //LiHua
console.log(person2.name); //Lisa
console.log(person1.colors); //[ 'red', 'green', 'blue', 'yellow' ]
console.log(person2.colors); //[ 'red', 'green', 'blue' ]

注意:person1.name的值,person2.name的值不互相影响,并不是因为person1person2有独立的 name 值,而是因为person1.name = 'person1',给person1添加了 name 值,并非修改了原型上的 name 值。

寄生式继承

function createPerson(original){
    var clone = Object.create(original)
    clone.sayGood = function(){
        console.log('hello');
    }
    return clone
}

let obj = {
    name: 'aaa'
}
let obj2 = createPerson(obj)

console.log(obj2.name);

缺点:

  • 每次创建对象都会创建一遍方法,做不到函数的复用

寄生组合式继承

Parent.prototype.sayname = function(){
    console.log(this.name);
}

function Parent(name){
    this.name = name
    this.colors = ['red', 'green', 'blue']
}

function Child(name, age){
    Parent.call(this,name)
    this.age = age
}

var anotherPrototype = Object.create(Parent.prototype)
Child.prototype = anotherPrototype
Child.prototype.constructor = Child
Child.prototype.sayAge = function(){
    console.log(this.age);
}

var p1 = new Child('LiHua', 18)
p1.colors.push('yellow')
p1.sayname() //LiHua
p1.sayAge() //18
console.log(p1.colors); //[ 'red', 'green', 'blue', 'yellow' ]
var p2 = new Child('Lisa', 20)
p2.sayname() //Lisa
p2.sayAge() //20
console.log(p2.colors); //[ 'red', 'green', 'blue' ]

组合继承最大的缺点是会调用两次父类构造函数。一次是设置子类型实例的原型的时候, 一次实在创建子类实例的时候。而寄生组合式继承只用调用一次父构造函数,并且因此避免了在 父类构造函数的prototype 上面创建不必要的、多余的属性。原型链能保持不变,所以能够正常使用 pof 和 isPrototypeOf。

有问题的地方欢迎大家指出,也希望大家能多和我讨论。