JS - 盗用构造函数、原型链继承和组合继承 【继承篇】

140 阅读1分钟

原型链的问题:

  1. 子类(或者子构造函数)无法向父类传递参数(或父类构造函数)
  2. 父类属性子类共享(可能因一个子实例修改了父类的引用属性,而导致其他子实例共享的属性也被修改)

思维导图

盗用构造函数

也叫经典继承:在子类构造函数中调用父级构造函数并向其父级传递参数,使用 apply 或者 call 方法指定新的上下文(this 改变)

优点: 可以向父类传递参数;解决了原型链共享父类属性

缺点: 不能访问父类原型上定义的方法

function Parent(name) {
  this.name = name;
  this.friends = ["f1", "f2"];
}

Parent.prototype.say = function () {
  log("parent_say");
};

function Son(name, other) {
  // 盗用构造函数的精髓, 指定 this 调用父类构造函数【可以向父类构造函数传参数】
  Parent.call(this, name);

  this.other = other;
}

const son1 = new Son("son1", "other_son1");
const son2 = new Son("son2", "other_son2");

son1.friends.push("son1_friend");

log(son1.friends); // log: [ 'f1', 'f2', 'son1_friend' ]
// 盗用构造函数继承避免了原型链继承 共享父类属性造成的问题
log(son2.friends); // log: [ 'f1', 'f2' ]

// 盗用构造函数不能访问父类的原型方法
log(son2.say); // log: undefined

组合继承

综合原型链和盗用构造函数,将两者优点集中在一起; 既可以通过子类构造函数向父类构造函数传递参数,又可以访问父级原型上的方法,实现了共享方法

function Parent(name) {
  this.name = name;
  this.friends = ["f1", "f2"];
}

Parent.prototype.say = function () {
  log("parent_pro_say");
};

function Son(name, other) {
  // 调用了父类构造函数
  Parent.call(this, name);
  this.other = other;
}

// 又调用了一次父类构造函数
Son.prototype = new Parent();

Son.prototype.run = function () {
  log("can_run_fast");
};

const son1 = new Son("son1", "son_other_1");
const son2 = new Son("son2", "son_other_2");

log(son1.friends); // log: [ 'f1', 'f2' ]
son1.friends.push("son1_new_friend");
log(son1.friends); // log: [ 'f1', 'f2', 'son1_new_friend' ]
son1.say(); // log: parent_pro_say

son1.run(); // log: can_run_fast
log(son2.friends); // log: [ 'f1', 'f2' ];  son1 和 son2 的属性独立