继承

77 阅读3分钟

es5继承

一、原型链继承

原型链继承的原理是直接让子类的原型对象指向父类的实例,当子类实例找不到对应的属性和方法时就回往它原型对象也就是父类实例上找,从而实现对父类属性和方法的继承。

//父类
function Parent(){
  this.name = 'tom';
  this.play = [1,2];
}
//子类
function Child(){}
//让子类的原型对象指向父类实例, 这样一来在Child实例中找不到的属性和方法就会到原型对象(父类实例)上寻找
Child.prototype = new Parent();

var s1 = new Child();
console.log(s1.name,s1.play);//tom [1,2]

优点:

  1. 父类新增原型方法和属性,子类都能访问到。
  2. 简单,易于实现。

缺点:

  1. 无法实现多继承。
  2. 由于原型中的引用值被共享,导致实例上的修改直接影响到原型。
  3. 创建子类实例时,无法向父类构造函数传参,即没有实现super()功能。

二、借用构造函数继承

即在子类的构造函数中调用父类的构造函数,并为其绑定子类的this,让父类的构造函数把成员属性和方法都挂到子类的this上。

function Parent(name){
  this.name = name;
  this.play = [1,2];
}
Parent.prototype.say = function () {
  console.log('Parent-say');
}
//子类
function Child(name){
  Parent.call(this, name)
}
let c1 = new Child()
let c2 = new Child()
//传参
let c3 = new Child('tom')
c1.say() //使用构造函数继承并没有访问到原型链,say 方法不能调用
console.log(c3.name) //tom
c1.play.push(3)
console.log(c1.play)// [1,2,3]
console.log(c2.play)// [1,2]
console.log(c1 instanceof Parent) //false

优点:

  1. 解决了原型链继承子类实例共享父类引用属性问题。
  2. 可以在子类构造函数中向父类构造函数中传参。
  3. 可以实现多继承(call多个父类对象)。

缺点:

  1. 实例并不是父类的实例,只是子类的实例。
  2. 只能继承父类实例属性和方法,不能继承原型属性和方法。

三、组合继承

主要思想是使用原型链实现对原型属性和方法的继承,借用构造函数实现对实例属性的继承。

function Parent(name) {
  this.name = name
}
Parent.prototype.getName = function() {
  return this.name;
}
function Child(name) {
  // 构造函数继承
  Parent.call(this, name) //第二次调用Parent
}
//原型链继承
Child.prototype = new Parent() //第一次调用Parent
Child.prototype.constructor = Child

优点:

  1. 可以继承实例属性和方法,也可以继承原型属性和方法。
  2. 不存在引用属性共享问题。
  3. 可传参。

缺点:

  1. 调用了两次父类构造函数(耗内存),生成了两份实例。 一份在当前的实例自己本身的,另一份在子类对应的原型对象中。但无需担心访问出现问题,因为默认一定是访问实例本身这一部分的。

四、寄生式组合继承

思想是借用构造函数来继承属性,使用混合式原型链继承方式。

function Parent(name) {
  this.name = [name]
}
Parent.prototype.getName = function() {
  return this.name
}
function Child() {
  // 构造函数继承
  Parent.call(this, 'tom') 
}
//原型链继承
// Child.prototype = new Parent()
Child.prototype = Object.create(Parent.prototype)  //将`指向父类实例`改为`指向父类原型`
Child.prototype.constructor = Child

目前最优的继承方式。

es6继承

es6引入class关键字,class可以通过extends关键字实现继承,还可以通过static关键字定义类的静态方法。 注意:class关键字只是原型的语法糖,js继承仍然是基于原型实现的。

class Parent {
  constructor(name,age){
    this.name = name
    this.age = age
  }
  sayName(){
    console.log(this.name);
  }
}
class Child extends Parent{
  constructor(name,age,gender){
    super(name,age)
    this.gender = gender
  }
  sayGender(){
    console.log(this.gender);
  }
}
const c1 = new Child('tom',18,'男')
c1.sayGender() //男
c1.sayName()  //tom
console.log(c1.name); //tom
console.log(c1.age); //18

对象方法补充

  1. hasOwnProperty:对象是否有一个属于自己的属性(不是在原型上的属性)
  2. in/for in :判断某个属性是否在某个对象或者对象原型上
  3. instanceof:用于检测构造函数的pototype,是否出现在某个实例对象的原型链上
  4. isPrototypeOf:用于检测某个对象,是否出现在某个实例对象的原型链上
  5. Object.create():静态方法以一个现有对象作为原型,创建一个新对象。