JS的继承(二)

304 阅读3分钟

这是我参与11月更文挑战的第5天,活动详情查看:2021最后一次更文挑战

介绍

  • 继承可以使子类得到父类的属性和方法,甚至可以重新定义父类的属性。
    • 通过重写或覆盖父类的方法,以取得不同于父类的属性和方法
  • 继承是面向对象的,可以更好的复用之前的开发代码,缩短开发周期,提高开发效率。

简单回顾

上一篇文章的内容中,我们学习了不适用Object.create()的几种继承方式:

  • 原型链继承
  • 构造函数继承
  • 由原型链继承和构造函数继承两种方式结合出的组合式继承

那接下来就开始学习新的继承内容了。

使用Object.create()

以下方法针对于普通对象。

Object.create 方法,这个方法接收两个参数:一是用作新对象原型的对象、二是为新对象定义额外属性的对象(可选参数)。

原型式继承

  let parent4 = {
    name: "parent4",
    friends: ["p1", "p2", "p3"],
    getName: function() {
      return this.name;
    }
  };
  let person4 = Object.create(parent4);
  person4.name = "tom";
  person4.friends.push("jerry");
  let person5 = Object.create(parent4);
  person5.friends.push("lucy");

  console.log(person4.name);  // tom
  console.log(person4.name === person4.getName());  // true
  console.log(person5.name);  // parent4
  console.log(person4.friends);  // ["p1", "p2", "p3", "jerry", "lucy"]
  console.log(person5.friends);  // ["p1", "p2", "p3", "jerry", "lucy"]

原型式继承,名字听起来似乎和原型链继承方式如出一辙,但是内容却是有很大差别。相对于原型链继承方式来说,代码结构简单了很多,但也有很大缺点:多个实例中引用类型数据的内存也是共享的,存在篡改的可能

原因就是Object.create是可以用来支持浅拷贝的。所以当实例中的属性值是引用类型数据时,内容共享,会导致发生篡改。

寄生式继承

其原理是:先使用原型式继承可以获得一份目标对象的浅拷贝,然后对浅拷贝对象的能力进行增强,给其添加一些方法,最终将扩展后的对象作为子类使用。

寄生式继承和原型式继承的优缺点相同,但是寄生式继承可以在父类的基础上扩展更多的方法,以便使用更加的灵活一些

   let parent5 = {
    name: "parent5",
    friends: ["p1", "p2", "p3"],
    getName: function() {
      return this.name;
    }
  };
  function clone(original) {
    let clone = Object.create(original);
    clone.getFriends = function() {
      return this.friends;
    };
    return clone;
  }
  let person5 = clone(parent5);

  console.log(person5.getName());
  console.log(person5.getFriends());

寄生组合式继承

 function Parent6() {
    this.name = 'parent6';
     this.play = [1, 2, 3];
  }
   Parent6.prototype.getName = function () {
    return this.name;
  }
  function Child6() {
    Parent6.call(this);
    this.friends = 'child5';
  }
  function clone (parent, child) {
    // 这里改用 Object.create 就可以减少组合继承中多进行一次构造的过程
    child.prototype = Object.create(parent.prototype);
    child.prototype.constructor = child;
  }
  clone(Parent6, Child6);
  Child6.prototype.getFriends = function () {
    return this.friends;
  }
  let person6 = new Child6();
  
  console.log(person6);{name: "parent6", play: [1,2,3], friends: "child5"}
  console.log(person6.getName());   //parent6
  console.log(person6.getFriends());  // child5

结合了寄生式继承和组合式继承的优点,通过寄生式继承减少了组合式继承过程中的构造函数调用过程,并且可以在父类的基础上对子类对象进行属性和方法的扩展,基本解决了前几种继承方式的缺点,较好地实现了继承想要的结果,减少了继承过程中性能的开销。

ES6extends继承

class Person {
  constructor(name) {
    this.name = name
  }
  // 原型方法
  // 即 Person.prototype.getName = function() { }
  // 下面可以简写为 getName() {...}
  getName = function () {
    console.log('Person:', this.name)
  }
}
class Gamer extends Person {
  constructor(name, age) {
    // 子类中存在构造函数,则需要在使用“this”之前首先调用 super()。
    super(name)
    this.age = age
  }
}
const asuna = new Gamer('Asuna', 20)
asuna.getName() // 成功访问到父类的方法

ES6中的extends继承和java的很相似。父类的原型方法写在构造函数之外,而子类想继承父类属性和方法,除了extends关键字之外。还必须在子类的构造函数中通过super()调用才能实现继承,再之后才是对子类对象自己的属性和方法进行定义。