[JS-OOP] 继承

Js是通过原型链实现继承的,有以下几种方式:

1、原型链继承:

方式:子类 的 prototype 属性指向 父类 的 一个实例

局限
(1)父类实例 的 实例属性 会变成 下方原型链 所有实例 的 原型属性
(2)创建子类实例时,不能自定义源自父类的实例属性,源自父类的实例属性只能采用默认值

function SuperType() {
    this.property = true;
}

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

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

// SubType继承自SuperType, 继承完再给原型添加方法
SubType.prototype = new SuperType();
SubType.prototype.getSubValue = function() {
    return this.sub_property;
}

let instance = new SubType();
console.log(instance.getSuperValue(), instance.getSubValue()); 
// true false

在这里插入图片描述
纯原型链继承的方式存在缺陷:

在JS中,一个实例有两种属性,实例属性、原型属性,可以理解为 实例属性为实例私有的,原型属性为同一个原型出来的实例中共享的属性。

但如果采用纯原型链继承的方式,SuperType的实例属性将直接变成SubType的原型属性,那么就会导致一些问题

如 小鸡,小鸭子都是继承自鸟类,鸟类有个实例属性 skills = [], 如果用纯原型链的方式继承的话,那么skills将成为禽类的原型方法,那么,当小鸡会喔喔叫的时候,小鸭子也会。当小鸭子会游泳的时候,小鸡也会,这就是缺陷。

假定没有上面这个问题,我们采用原型链继承的方式也不能在初始化小鸭子,小鸡的时候定制skills,如何解决呢?这引出了下面的盗用构造函数方法

2、盗用构造函数继承

// 盗用构造函数实现继承

function SuperType() {
    this.skills = [];
}

function SubType() {
    // 盗用父类构造函数
    SuperType.call(this);
}

const hen = new SubType();
const duck = new SubType();

hen.skills.push("jiji");
duck.skills.push("swim");

console.log(hen.skills, duck.skills);
// [ 'jiji' ] [ 'swim' ]

方式:在子类构造函数中使用 call 调用 父类构造函数 给this添加自定义的父类实例属性

缺陷
盗用构造函数成功解决了 子类创建实例时无法自定义源自父类的实例属性 这个问题!

但这样做无法继承到父类的原型属性,有什么好办法?

答案是配合原型链继承一起使用。

我们会发现,盗用构造函数后,子类实例中的父类实例属性 会 遮蔽 SubType.protoype = new SuperType(); 中的实例属性。

因此只能继承到 原型方法!

这就是 组合继承

3、组合继承

// 组合继承

function SuperType(name) {
    this.name = name;
    this.skills = [];
}

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

function SubType(name, color) {
    // 继承父类的实例属性
    SuperType.call(this, name);
    this.color = color;
}

// 使用原型链继承原型属性
SubType.prototype = new SuperType();
SubType.prototype.sayColor = function() {
    return this.color;
}

const hen = new SubType("hen", "red");
const duck = new SubType("duck", "yellow");

hen.skills.push("wowo");
duck.skills.push("swim");

console.log(hen.skills, duck.skills); // [ 'wowo' ] [ 'swim' ]

console.log(hen.sayName(), duck.sayName()); // hen duck

console.log(hen.sayColor(), duck.sayColor());   // red yellow

在这里插入图片描述
实现:原型链继承 + 盗用构造函数遮蔽父类实例的实例属性

缺陷:调用了 2 次父类构造函数

如何优化?

道格拉斯提出了原型式集成,也就是我们现在常用的 let res = Object.create(obj);

res 的 _proto_ 指向 obj 从而实现了无需父类构造函数的 对象层面上的继承

实现机制类似以下代码:

function object(obj) {
    function fn() {};
    fn.prototype = obj;
    return new fn();
}

而寄生式继承是在原型式继承上提出的扩充,在 实现 对象继承 后再给新对象添加属性、方法等,最后返回一个 有新属性、新方法的 新对象。

let bird = {
    name: "normal bird",
    skills: []
};

function createDuck(obj) {
    let duck = Object.create(obj);
    duck.swim = () => console.log("duck can swim.");
    duck.skills.push("swim");
    return duck;
}

const duck = createDuck(bird);

console.log(duck, duck.skills);     // { swim: [Function] } [ 'swim' ]
duck.swim();                        // duck can swim.

这样,我们就能引出我们的优化方案:寄生式组合继承

4、寄生式组合继承

实例属性仍然使用盗用构造函数进行继承

原型属性使用寄生式继承实现,而非组合继承中 SubType.prototype = new SuperType();

这样改进解决了组合继承的什么问题呢?

组合继承使用 new SuperType() 的方式,导致了会调用两次父类构造函数,这样有效率问题。另外,SubType原型上会有不必要的SuperType实例属性。

而寄生式组合继承 只需要在盗用构造函数时只调用一次父类构造函数,而且Subtype的原型仅有SuperType原型中的属性。

function SuperType(name) {
    this.name = name;
    this.skills = [];
}
// 这里不能使用 箭头函数 否则返回undefined 箭头函数作用域中无this,像上一层作用域,也就是全局作用域查找 this.name 无
SuperType.prototype.sayName = function() {
    return this.name;
}
const bird = new SuperType("bird");
console.log(bird, bird.sayName());          // SuperType { name: 'bird', skills: [] } bird
function SubType(name, age) {
    // 盗用构造函数 以继承父类构造函数的实例方法
    SuperType.call(this, name);
    this.age = age;
}
function inheritPrototype(Sub, Super) {
    let prototype = Object.create(Super.prototype);
    prototype.constructor = Sub;
    Sub.prototype = prototype;
}
// 使用寄生式继承 完成 对父类原型属性的继承
inheritPrototype(SubType, SuperType);
SubType.prototype.sayAge = function() {
    return this.age;
}
const duck = new SubType("duck", 8);
console.log(duck, duck.sayName(), duck.sayAge());   //SubType { name: 'duck', skills: [], age: 8 } duck 8

寄生式组合继承是最佳的继承方式!

请添加图片描述

5、es6继承语法糖

方式:使用 extends 单继承,使用 super(参数列表) 盗用构造函数,super() 要放在最前面

class Super() {}

class Sub() {
	constructor() {
		super();
		this.a = 1;
	}
}

实现抽象类,不可直接构造:

class Super() {
	if(new.target === Super) throw new Error("Super cannout be instanced directly");
}

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值