父类
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,因为在这一通操作里,这一个是属于父类的,也就是指向父类的构造函数