js的几种继承方式

目录

js继承

原型链继承

子类原型指向父类的一个实例对象。子类会继承父类实例上的属性和方法,也可以访问父类原型上的属性和方法。

如果子类原型上想要覆盖父类的方法/添加父类没有的方法,这些方法必须在原型赋值之后添加,否则无效。

function SuperType () {
    this.prop = true;
    this.colors = ['red', 'green', 'blue'];
}

SuperType.prototype.getSuperValue = function () {
    return this.prop;
}

function SubType () {
    this.subProp = false;
}

// 继承SuperType
SubType.prototype = new SuperType();

// 新方法
SubType.prototype.getSubValue = function () {
    return this.subProp;
}

// 覆盖已有方法
SubType.prototype.getSuperValue = function () {
    return false;
}

const instance = new SubType();
console.log(instance.getSuperValue()); // false

const instance2 = new SubType();
instance2.colors.push('black');
console.log(instance2.colors); // "red,green,blue,black"

const instance3 = new SubType();
console.log(instance3.colors); // "red,green,blue,black"

console.log(SubType.prototype.constructor === SuperType); // true

缺点

  • 如果父类实例上存在引用属性,子类的所有实例对象都会共享父类实例上的引用属性(父类实例属性 => 子类原型属性)
  • 创建子类实例对象时不能给父类构造函数传递参数
  • 重写原型导致constructor丢失

盗用构造函数继承

子类继承父类实例属性和方法,可以在创建子类实例时给父类构造函数传递参数。

function SuperType (name) {
    this.name = name;
    this.colors = ['red', 'green', 'blue'];
    this.sayYes = function () {
        console.log('yes');
    }
}

SuperType.prototype.sayNo = function () {
  console.log('no');
}

function SubType () {
    // 继承SuperType
    SuperType.call(this, 'JavaScript');
    this.age = 30;
}

const instance = new SubType();
instance.colors.push('black');
console.log(instance.colors); // "red,green,blue,black"

const instance2 = new SubType();
console.log(instance2.colors); // "red,green,blue"

const instance3 = new SubType();
console.log(instance3.name); // "JavaScript"
console.log(instance3.age); // 30

console.log(instance instanceof SubType); // true
console.log(instance instanceof SuperType); // false
console.log(SubType.prototype.isPrototypeOf(instance)); // true
console.log(SuperType.prototype.isPrototypeOf(instance)); // false

instance.sayYes() // yes
console.log(instance.sayNo); // undefined

优点

  • 避免了父类实例上的引用属性被子类所有实例对象共享

缺点

  • 最多只能继承父类在构造函数中定义的属性和方法
  • 不能访问父类原型上定义的属性和方法
  • 没有保留instanceof操作符和isPtototypeOf()方法识别合成对象的能力

组合继承

原型链继承 + 盗用构造函数继承

  • 原型链继承:继承父类原型上的属性和方法

  • 盗用构造函数继承:继承父类实例上的属性

function SuperType (name) {
    this.name = name;
    this.colors = ['red', 'green', 'blue'];
}

SuperType.prototype.sayName = function () {
    console.log(this.name);
}

function SubType (name, age) {
    // 继承SuperType上的属性
    SuperType.call(this, name);
    this.age = age;
}

// 继承父类原型上的属性和方法
SubType.prototype = new SuperType();

SubType.prototype.sayAge = function () {
    console.log(this.age);
}

const instance = new SubType('JavaScript', 30);
instance.colors.push('black');
console.log(instance.colors); // "red,green,blue,black"
instance.sayName(); // "JavaScript"
instance.sayAge(); // 30

const instance2 = new SubType('ECMA Script', 40);
console.log(instance2.colors); // "red,green,blue"
instance2.sayName(); // "ECMA Script"
instance2.sayAge(); // 40

console.log(SubType.prototype.constructor === SuperType); // true

优点

  • 可以继承父类上的原型和方法
  • 避免了父类实例上的引用属性被子类所有实例对象共享

缺点

  • 创建子类实例对象时,父类构造函数总是会被调用两次(效率问题)
  • 重写原型导致constructor丢失

原型式继承

不自定义类型也可以通过原型实现对象之间的信息共享。

适用于不要单独创建构造函数。在一个对象的基础上创建一个新对象

ES6中与Object.create()方法效果相同

function object (o) {
    function F () {}
    F.prototype = o;
    return new F();
}

const person = {
    name: 'Script',
    colors: ['red', 'green', 'blue']
};

const person2 = object(person);
person2.name = 'JavaScript';
person2.colors.push('black');

const person3 = object(person);
person3.name = 'ECMA Script';
person3.colors.push('yellow');

console.log(person.colors); // "red,green,blue,black,yellow"

缺点

  • 关于继承的引用属性同原型链继承一致

寄生式继承

原型式继承相似

思路:

  • 创建一个实现继承的函数,接收一个对象作为参数
  • 在函数中以某种方法增强对象参数,返回这个对象
function object (o) {
    function F () {}
    F.prototype = o;
    return new F();
}

function createAnother(original) {
    const clone = object(original);
	// 增强对象
    clone.sayHi = function () {
        console.log('hi');
    }
    return clone;
}

缺点

  • 给对象添加函数会导致函数难以复用

寄生式组合继承

盗用构造函数继承属性 + 混合式原型链继承属性和方法

盗用构造函数继承 + 寄生式继承

思路:

  • 寄生式继承方式继承父类原型
  • 将返回的父类原型副本赋值给子类原型
// 继承父类原型属性和方法
function inheritPrototype(subType, superType) {
    const protoType = object(superType.prototype); // 创建父类原型副本对象
    protoType.constructor = subType;
    subType.prototype = protoType;
}

function SuperType (name) {
    this.name = name;
    this.colors = ['red', 'green', 'blue'];
    this.sayYes = function () {
        console.log('yes');
    }
}

SuperType.prototype.sayNo = function () {
    console.log('no');
}

function SubType (name, age) {
    SuperType.call(this, name);
    this.age = age;
}

inheritPrototype(SubType, SuperType);

const instance = new SubType('JavaScript', 30);
instance.sayYes(); // yes
instance.sayNo(); // no

优点

  • 可以继承父类上的原型和方法
  • 避免了父类实例上的引用属性被子类所有实例对象共享
  • 创建子类实例对象时,父类构造函数仅被调用一次
上一篇:iOS底层原理(二)KVO和KVC


下一篇:CF474E Pillars(离散化+线段树+保存DP路径)