JS 常见的六种继承方式

242 阅读2分钟

JS 实现继承常见的六种方式

一、原型链

先简单描述一下原型链,每个构造函数都有一个原型对象,原型对象又有一个 constructor 属性指向 构造函数,而每个构造函数实例对象都有一个 __proto__ 属性指向原型对象,如果当前实例队对象上没有这个属性,就会到它的原型对象中去找,而它的原型对象也是一个实例,就会重复之前步骤,直到找到 Object 的原型对象还没有找到,则返回 undefined。

function Parent() {
    this.name = 'parent';
    this.nums = [1, 2, 3];
}

function Child() {
    this.type = 'child'
}
Child.prototype = new Parent(); 
Child.prototype.constructor = Child;
  let c1 = new Child();
  let c2 = new Child();
  c1.nums.push(4);
  console.log(c1, c2);

原型链继承

缺点:因为两个实例使用的是同一个原型对象。它们的内存空间是共享的,一个变化回引起另一个也发生变化。

二、构造函数继承

function Parent() {
    this.name = 'parent';
    this.nums = [1, 2, 3];
}

function Child() {
    Parent.call(this);
    this.type = 'child'
}
  let c1 = new Child();
  let c2 = new Child();
  c1.nums.push(4);
  console.log(c1, c2);
  console.log(c1.getName());

构造函数继承 缺点:无法访问到父类原型对象的属性,虽然解决了共享的问题。

三、组合继承

function Parent() {
    this.name = 'parent';
    this.nums = [1, 2, 3];
}
Parent.prototype.getName = function() {
    return this.name;
}

function Child() {
    Parent.call(this);
    this.type = "child";
}
Child.prototype = new Parent();
Child.prototype.constructor = Child;
  let c1 = new Child();
  let c2 = new Child();
  c1.nums.push(4);
  console.log(c1, c2);
  console.log(c1.getName());

缺点:虽然组合继承解决了前两个问题,但是 Parent 执行了两次,第一次是通过 call 方法,第二次改变 Child 是 prototype 的时候,而多执行一次就多了一次新能的开销

四、原型式继承

原型式继承就是同通过 ES5 里面的 Object.create 方法来实现,方法创建一个新对象,使用现有的对象来提供新创建的对象的__proto__Object.create()

这里我们来看一下普通对象是如何实现继承的。

  let Parent = {
    name: "parent",
    nums: [1, 2, 3],
    getName: function () {
      return this.name;
    },
  };
  let person1 = Object.create(Parent);
  let person2 = Object.create(Parent);
  person1.nums.push(4);
  person1.name = "tom";
  console.log(person1, person2);
  console.log(person1.getName());

缺点:Object.create 属于浅拷贝,还是会存在内容共享的问题

五、寄生式继承

使用原型式继承可以获得一份目标对象的浅拷贝,然后利用这个浅拷贝的能力再进行增强,添加一些方法,这样的继承方式就叫作寄生式继承。

虽然其优缺点和原型式继承一样,但是对于普通对象的继承方式来说,寄生式继承相比于原型式继承,还是在父类基础上添加了更多的方法。

  let Parent = {
    name: "parent",
    nums: [1, 2, 3],
    getName: function () {
      return this.name;
    },
  };

  function clone(original) {
    let clone = Object.create(original);
    clone.getFriends = function () {
      return this.friends;
    };
    return clone;
  }

  let p1 = new clone(Parent);

六、寄生组合式继承

结合前几种中提及的继承方式优缺点进行更改,这也是所有继承方式里面相对最优的继承方式。

  function Parent() {
    this.name = "parent";
    this.play = [1, 2, 3];
  }
  Parent.prototype.getName = function () {
    return this.name;
  };
  function Child() {
    Parent.call(this);
    this.friends = "child";
  }
  Child.prototype = Object.create(Parent.prototype);
  Child.prototype.constructor = Child;
  Child.prototype.getFriends = function () {
    return this.friends;
  };
  let c1 = new Child();
  let c2 = new Child();
  c1.play.push(4);
  console.log(c1.getName());
  console.log(c1.getFriends());
  console.log(c1, c2);