继承

2020 M12 22

原型链继承

原型中包含的引用值会在所有实例间共享,所以属性通常会在构造函数中定义而不会定义在原型上。 在使用原型实现继承时,原型实际上变成了另一个类型的实例。 这意味着原先的实例属性摇身一变成为了原型属性。
function SuperType() {
    this.colors = ["red", "blue", "green"];
}
function SubType() { }
// 继承 SuperType
SubType.prototype = new SuperType();
let instance1 = new SubType();
instance1.colors.push("black");
console.log(instance1.colors); // "red,blue,green,black"
let instance2 = new SubType();
console.log(instance2.colors); // "red,blue,green,black"


存在的问题

引用共享:在这个例子中,SuperType构造函数定义了一个colors属性,其中包含一个数组(引用值)。 每个SuperType的实例都会有自己的colors属性,包含自己的数组。 但是,当SubType通过原型继承SuperType后,SubType.prototype变成了SuperType的一个实例,因而也获得了自己的colors属性。 这类似于创建了SubType.prototype.colors属性。 最终结果是,SubType的所有实例都会共享这个colors属性。 这一点通过instance1.colors上的修改也能反映到instance2.colors上就可以看出来。
无法在子类构造函数中向父类构造函数传参

构造函数继承

目的:为了解决原型链继承存在的两个问题,出现了构造函数继承
思路:在子类构造函数中调用父类构造函数,各自绑定上下文,无干扰
function SuperType(name) {
    this.colors = ["red", "blue", "green"];
    this.name=name
}

//不写在构造函数中的属性,构造函数继承是获取不到的
SuperType.prototype.age=18
//不写在构造函数中的方法,构造函数继承是获取不到的
SuperType.prototype.say=function(){
    console.log('say super')
}

function SubType() {
    // 使用构造函数继承 SuperType
    SuperType.call(this,'tom');
}
var instance1 = new SubType();
instance1.colors.push("black");
console.log(instance1.colors); // "red,blue,green,black"
var instance2 = new SubType();
console.log(instance2.colors); // "red,blue,green"
console.log(instance2.name); // "tom"
console.log(instance2.say); // undefined
console.log(instance2.age); // undefined

存在问题

必须在构造函数中定义属性和方法,导致函数不能重用
子类不能访问父类原型上定义的属性和方法

组合继承

原型链继承和构造函数继承是刚好互补的。组合继承就是将原型链和构造函数两者的优点集中了起来。 思路是使用原型链继承原型上的属性和方法,而通过构造函数继承实例属性。 这样既可以把方法定义在原型上以实现重用,又可以让每个实例都有自己的属性。
function SuperType(name) {
    this.name = name;
    this.colors = ["red", "blue", "green"];
}
SuperType.prototype.sayName = function () {
    console.log(this.name);
};
function SubType(name, age) {
    // 继承属性
    SuperType.call(this, name); //父类构造函数被执行
    this.age = age;
}
// 继承方法  子类构造器未修正
SubType.prototype = new SuperType(); //父类构造函数被执行

SubType.prototype.sayAge = function () {
    console.log(this.age);
};
var instance1 = new SubType("Nicholas", 29);
instance1.colors.push("black");
console.log(instance1.colors); // "red,blue,green,black"
instance1.sayName(); // "Nicholas";
instance1.sayAge(); // 29
var instance2 = new SubType("Greg", 27);
console.log(instance2.colors); // "red,blue,green"
instance2.sayName(); // "Greg";
instance2.sayAge(); // 27
组合继承弥补了原型链和盗用构造函数的不足,是 JavaScript 中使用最多的继承模式。 而且组合继承也保留了 instanceof 操作符和 isPrototypeOf() 方法识别合成对象的能力。 看似完美,却并不完美。父类构造函数被执行两次,子类构造器未修正。

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

寄生式组合继承通过构造函数继承属性,但使用混合式原型链继承方法。 基本思路是不通过调用父类构造函数给子类原型赋值,而是取得父类原型的一个副本。 说到底就是使用寄生式继承来继承父类原型,然后将返回的新对象赋值给子类原型。

function SuperType(name) {
    this.name = name;
    this.colors = ["red", "blue", "green"];
}
SuperType.prototype.sayName = function () {
    console.log(this.name);
};
function SubType(name, age) {
    SuperType.call(this, name);
    this.age = age;
}
//取得父类原型的一个副本 
SubType.prototype=Object.create(SuperType.prototype)
//无副作用的修正构造器指向
//原型继承修正会导致一变皆变
SubType.prototype.constructor=SubType 
SubType.prototype.sayAge = function () {
    console.log(this.age);
};

extends关键字

function A(){
    //相当于super
    B.call(this,18)
    _extends(A, B)//extends
}
function B(age){
    this.age=age
}

function _extends(subClass, superClass) {
  subClass.prototype = Object.create(superClass && superClass.prototype, {
    constructor: {
      value: subClass,
      writable: true,
      configurable: true
    }
  });
}