| JavaScript对话面试官系列 | - 七大继承

133 阅读4分钟

原型链继承

实现方法: 将子类的原型对象关联到父类的实例对象当中Children.prototype = new Parent();

优点: 实现了继承,并且子类可以共享父类原型上的属性和方法,

缺点: 但是引用类型的属性被所有实例共享存在安全问题 , 而且在创建子类实例的时候, 不能向 parent 当中传参,只能使用定义好的属性和方法。

//原型链继承
function Parent() {
  this.collection = ['ryan', 'mike'];
}
Parent.prototype.eating = function () {
  console.log(this.name + 'is eating');
};
function Children(name) {
  this.name = name;
}
// 关键代码
Children.prototype = new Parent();
const child_1 = new Children('clc');
child_1.eating(); // clc在吃饭;

借用构造函数继承

实现方法: 在子类构造函数当中用 call 调用父类构造函 Parent.call(this,参数)

优点:避免了引用类型属性被实例共享的安全问题,并且也做到了可以向 parent 当中传递参数

缺点:但是缺点就是方法在构造函数当中定义, 没有利用原型, 每创建一个实例就会创建一遍方法

// 借用构造函数继承
function Parent(name, age) {
  this.name = name;
  this.age = age;
  this.collection = ['ryan', 'mike'];
  this.eating = function () {
    console.log(this.name + 'is eating');
  };
}
function chilren(name, age) {
  Parent.call(this, name, age);
}
const child_1 = new Children('clc', 18);

组合继承

  • 实现方法:将原型链继承和借用构造函数结合起来。
  • 优点:融合原型链继承和构造函数的优点(既可以传递参数调用父构造函数,又可以继承父构造函数原型上的方法,避免了引用类型属性被实例共享),
  • 缺点:缺点是存在效率问题, 父构造函数会调用两次,导致子类实例和原型上存在同名属性。
function Parent(name, age) {
  this.name = name;
  this.age = age;
  this.collection = ['ryan', 'mike'];
}
Parent.prototype.eating = function () {
  console.log(this.name + 'is eating');
};
// 关键代码
function Children() {
  Parent.call(this, name, age);
}
Children.prototype = new Parent();
const child_1 = new Children();

原型式继承

  • 实现思路:就是规范化的 Object.create(o)返回一个对象, 这个对象的原型是 o,__proto__指向 o。
  • 适用于你有一个对象在他的基础上再创建一个对象
function myObjectCreate(o) {
  function F() {}
  F.prototype = o;
  return new F();
}
// 等同于 object.create(o)

寄生式继承

  • 实现思路:在原型式继承的基础上以某种方式增强对象, 最后返回对象
  • 缺点: 在该对象上添加的方法难以重用。
// 寄生式继承
function jicheng(o) {
  const clone = myObjectCreate(o);
  clone.sayHi = function () {
    console.log('你好');
  };
  return clone;
}

寄生式组合继承

  • 实现方式:通过寄生式继承来增强子类原型对象。和原型式类比,不直接将父类的实例关联到子类的原型对象上,而是将一个父类原型对象,通过Object.create()关联到子类的原型对象上。
  • 最佳实践,不会像组合继承那样调用两次父构造函数,也不会像原型链继承那样在继承的时候会共享父类引用类型的属性,并且既可以向父类构造函数传递参数,又可以通过原型对象来共享属性和方法。
// 寄生式组合继承
function myObjectCreate(o) {
  function F() {}
  F.prototype = o;
  return new F();
}
function inherit(son, parent) {
  son.prototype = myObjectCreate(parent.prototype);
  Object.defineProperty(son.prototype, 'constructor', {
    enumerable: false,
    configurable: true,
    writable: true,
    value: son,
  });
}

类继承

类是构造函数和原型的语法糖,babel在进行编译的时候,也是将类编译成了构造函数,extends关键字被编译为寄生组合式继承。

类继承的babel编译流程是怎么样的。最核心的就是super,为什么每一次继承前,都要先super调用父类构造函数?因为一般的继承都是先创建子类的实例,在通过继承的手段去调用父类的构造函数。

而类在编译的时候是通过Reflect.constructro()这个方法,是通过先调用父类构造函数来创建出子类的实例对象。所以我们每次先super一下,才能拿到子类的实例对象,这也是为什么this要放在super之后进行调用。

var Student = /*#__PURE__*/ (function (_Person) {
  // 实现之前的寄生式继承的方法(静态方法的继承)
  _inherits(Student, _Person);

  var _super = _createSuper(Student);

  function Student(name, age, sno) {
    var _this;

    _classCallCheck(this, Student);

    // Person不能被当成一个函数去调用
    // Person.call(this, name, age)

    debugger;
    // 创建出来的对象 {name: , age: }
    _this = _super.call(this, name, age);
    _this.sno = sno;
    // {name: , age:, sno: }
    return _this;
  }

  _createClass(Student, [
    {
      key: "studying",
      value: function studying() {
        console.log(this.name + " studying~");
      },
    },
  ]);

  return Student;
})(Person);

var stu = new Student();