Skip to content

20210906152341

通过 Object.create 来划分不同的继承方式,最后的寄生式组合继承方式是通过组合继承改造之后的最优继承方式,而 extends 的语法糖和寄生组合继承的方式基本类似

<!-- more -->

原型链继承

js
function Parent() {
  this.name = "parent";
  this.play = [1, 2, 3];
}

function Child() {
  this.type = "child";
}

Child.prototype = new Parent();
Child.prototype.constructor = Child;
console.log(new Child());

缺点:公用一个原型对象

构造函数继承(借助 call)

js
function Parent() {
  this.name = "parent";
  this.getName = function() {
    return this.getName;
  };
}

Parent.prototype.getName = function() {
  return this.name;
};

function Child() {
  Parent.call(this);
  this.type = "child";
}

console.log(new Child());

缺点:只能继承父类的实例属性和方法,不能继承原型属性或者方法

组合继承(Object.create)

js
function Parent() {
  this.name = "parent";
  this.play = [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;

const s1 = new Child();
const s2 = new Child();
s1.play.push(4);

console.log(s1.play, s2.play);
console.log(s1.getName());
console.log(s2.getName());

20210906150920

缺点: parent 执行了两次:第一次是改变 child 的 prototype 的时候,第二次是通过 call 方法调用 Parent 的时候

上面介绍的更多是围绕着构造函数的方式,那么对于 JavaScript 的普通对象,怎么实现继承呢?

原型式继承

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

Object.create是浅拷贝

js
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);
console.log(person4.name === person4.getName());
console.log(person5.name);
console.log(person4.friends);
console.log(person5.friends);

寄生组合式继承

js
function Parent() {
  this.name = "parent";
  this.play = [1, 2, 3];
}

Parent.prototype.getName = function() {
  return this.name;
};

function Child() {
  Parent.call(this);
  this.type = "child";
}

Child.prototype = Object.create(Parent.prototype);
Child.prototype.constructor = Child;

ES6 的 extends 关键字

js
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(); // 成功访问到父类的方法

bable 把 ES6 的代码翻译成 ES5 之后

js
function _possibleConstructorReturn(self, call) {
  // ...
  return call && (typeof call === "object" || typeof call === "function")
    ? call
    : self;
}

function _inherits(subClass, superClass) {
  // 这里可以看到
  subClass.prototype = Object.create(superClass && superClass.prototype, {
    constructor: {
      value: subClass,

      enumerable: false,

      writable: true,

      configurable: true,
    },
  });
  if (superClass)
    Object.setPrototypeOf
      ? Object.setPrototypeOf(subClass, superClass)
      : (subClass.__proto__ = superClass);
}

var Parent = function Parent() {
  // 验证是否是 Parent 构造出来的 this
  _classCallCheck(this, Parent);
};

var Child = (function(_Parent) {
  _inherits(Child, _Parent);
  function Child() {
    _classCallCheck(this, Child);
    return _possibleConstructorReturn(
      this,
      (Child.__proto__ || Object.getPrototypeOf(Child)).apply(this, arguments)
    );
  }
  return Child;
})(Parent);

参考

3d467cff7d26d2abdb58dea74114855