继承 extends

1. 继承实现方式(一):原型链 / 构造函数 / 组合

原型链继承

将子类原型绑定到父类实例上:

function Parent() {
  this.name = 'parent1';
  this.play = [1, 2, 3];
}
function Child() {
  this.type = 'child2';
}
Child.prototype = new Parent();

const s1 = new Child();
const s2 = new Child();
s1.play.push(4);
console.log(s1.play, s2.play); // [1, 2, 3, 4]、[1, 2, 3, 4]

缺点:改变 s1.play,s2.play 也跟着变,因为两个实例共用同一个原型对象,引用类型共享内存。

构造函数继承

借助 call 调用父类构造函数:

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

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

const child = new Child();
console.log(child);          // { name: 'parent1', type: 'child' }
console.log(child.getName()); // 报错:getName is not a function

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

组合继承

结合上面两种方式:

function Parent3() {
  this.name = 'parent3';
  this.play = [1, 2, 3];
}
Parent3.prototype.getName = function () { return this.name; };

function Child3() {
  Parent3.call(this);          // 第二次调用 Parent3
  this.type = 'child3';
}
Child3.prototype = new Parent3();  // 第一次调用 Parent3
Child3.prototype.constructor = Child3;

const s3 = new Child3();
const s4 = new Child3();
s3.play.push(4);
console.log(s3.play, s4.play); // [1,2,3,4]、[1,2,3]  不互相影响
console.log(s3.getName());     // 'parent3'

缺点:父类被执行了两次,造成多构造一次的性能开销。

2. 继承实现方式(二):原型式 / 寄生式 / 寄生组合式

原型式继承

借助 Object.create:

const parent4 = {
  name: 'parent4',
  friends: ['p1', 'p2', 'p3'],
  getName() { return this.name; }
};

const person4 = Object.create(parent4);
const person5 = Object.create(parent4);

缺点:Object.create 是浅拷贝,多个实例引用类型属性指向同一块内存。

寄生式继承

const parent5 = {
  name: 'parent5',
  friends: ['p1', 'p2', 'p3'],
  getName() { return this.name; }
};

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

const person5 = clone(parent5);

缺点:同原型式继承,浅拷贝。

寄生组合式继承(最优解)

借助 Object.create 改造,是所有继承方式里最优的。

function clone(parent, child) {
  child.prototype = Object.create(parent.prototype);
  child.prototype.constructor = child;
}

function Parent6() {
  this.name = 'parent6';
  this.play = [1, 2, 3];
}
Parent6.prototype.getName = function () { return this.name; };

function Child() {
  Parent6.call(this);
  this.friend = 'child5';
}
clone(Parent6, Child);

const person = new Child();

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

来源整理自:我的有道云笔记