js实现继承的方法

时间:2021-08-20
本文章向大家介绍js实现继承的方法,主要包括js实现继承的方法使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

父类

function Animal(name){
    this.name=name
}
Animal.prototype.eat=function(food){
    console.log(this.name+'吃'+food)
}

原型链继承

function Cat(){}
Cat.prototype=new Animal()
var cat=new Cat()
console.log(cat.name)
console.log(cat.eat('鱼'))
console.log(cat instanceof Animal); //true
console.log(cat instanceof Cat); //true

 

特点:

1,经典的继承关系,实例是子类的实例也是父类的实例

console.log(cat instanceof Animal); //true 
console.log(cat instanceof Cat); //true

2,父类新增的原型属性,子类也能访问(原型链)

缺点:

1,由于prototype被new Animal('cat')占据,所以子类想要拓展新属性或者方法必须在new Animal('cat')之后,灵活度受限

2,只能继承一个

3,由于继承自父类的实例对象,子类已经没办法通过参数修改子类实例的属性,比如我想创建name为小白的实例,但是修改的方法在父类那里,子类无法在创建时赋予name属性,

4,会将父级实例对象的属性继承至原型,导致所有实例共享,比如上述的子类就继承了父类实例的name=undefinde

构造继承

function Cat(name){
    Animal.apply(this,arguments)
}
var cat = new Cat('Tom');
console.log(cat.name);
console.log(cat.eat('鱼'))//报错
console.log(cat instanceof Animal); // false
console.log(cat instanceof Cat); // true

特点:

1,能够多继承(使用多个call或者apply)

2,能够传递参数进属于父类的函数

缺点:

1,实例是子类的实例,不是父类的实例

2,只能继承父类的实例属性和方法,不能继承父类的原型属性方法

3,也由于如此,这种继承方法只能将父类的实例函数继承为子类的实例函数,没办法继承原型上的方法,没办法复用函数

4,每个子类都有父类实例函数的副本,影响性能

实例继承

function Cat(){
    var instance=new Animal()
    instance.name=name||'Tom'
    return instance
}

// Test Code
var cat = new Cat();
console.log(cat.name);
console.log(cat.sleep());
console.log(cat instanceof Animal); // true
console.log(cat instanceof Cat); // false

特点

1,不限制调用方法,无论是直接调用还是new都能拿到实例对象

缺点

1,实例是父类的实例,跟子类没关系,所以子类也没办法拓展原型

2,不支持多继承

拷贝继承

function Cat(name){
    var animal=new Animal()
    for(let key in animal){
        Cat.prototype[key]=animal[key]
    }
    this.name=name
}

let cat=new Cat()
console.log(cat.name)
console.log(cat.eat())
console.log(cat instanceof Animal)
console.log(cat instanceof Cat)

特点

1,支持多继承

缺点

1,效率低,内存占用高,每个子类都需要实例化父类,并且循环拷贝

2,无法获取父类不可枚举的属性,其中包含父类的原型

组合继承

function Cat(name){
    Animal.apply(this,arguments);
    this.name = name || 'Tom';
}
Cat.prototype=new Animal()
Cat.prototype.constructor=Cat

var cat = new Cat();
console.log(cat.name);
console.log(cat.sleep());
console.log(cat instanceof Animal); // true
console.log(cat instanceof Cat); // true

特点

1,这是构造继承+原型链继承的组合,弥补了构造继承的缺点无法继承父类的原型

2,实例既是父类的实例也是子类的实例

3,可以传参

4,继承自父类的原型可复用

缺点

1,调用了两次父类,消耗内存

寄生组合继承

function Cat(name){
   Animal.apply(this,arguments)
}

(function(){
    var Super=function(){}
    Super.prototype=Animal.prototype
    Cat.prototype = new Super();
})()

var cat = new Cat();
console.log(cat.name);
console.log(cat.eat());
console.log(cat instanceof Animal); // true
console.log(cat instanceof Cat); //true

特点

各方面都较为完善,可以继承自父类的构造属性和函数与父类的原型属性和函数,且避免了共享父级构造属性函数的情况(使用的super是一个空的构造函数),同时子类的参数也能参与父类的函数,并且子类实例也是父类的实例

缺点:

  1,实现复杂,需要每个子类需要执行一次父类,一次匿名函数

问题,为何在实现继承时,不能让子类的原型对接父类的原型

问这个问题前首先要明白继承后我们想拿到什么结果

1,子类获取父类的所有属性与方法包括原型上的

2,子类与父类要形成继承关系,即子类的实例也是父类的实例,实例能通过原型链追寻到父类

然后在想直接使用父类的原型有什么后果

1,首先一点原型也是对象的属性,它是引用类型,所以子类与父类的prototype直接对接上会导致子类没办法修改原型参数,因为一旦修改了也是修改了父类的原型,由此也引发另外一个问题,子类失去了对应它构造函数的constructor,因为在这一通操作里,这一个是属于父类的,也就是指向父类的构造函数

原文地址:https://www.cnblogs.com/zsckl/p/15166376.html