继承的多种⽅式&优缺点

继承是什么?

继承(inheritance)是面向对象软件技术当中的一个概念。继承可以使得子类具有父类别的各种属性方法,而不需要再次编写相同的代码。

如果一个类别B"继承自"另一个类别A,就把这个B称为"A的子类",而把A称为"B的父类别"也可以称"A是B的超类"。

继承的多种方式

故事背景:作者有两只喵,大儿金吉拉(小白),小儿布偶猫(小布),默认他们的爸爸都爱吃饭和睡觉,毕竟他们也爱吃饭和睡觉,大儿聒噪(哼小曲),小儿优雅乖巧,论二胎家庭是无法做到一碗水端平的吧

1. 原型链继承(Prototype Inheritance):

  • 实现方式: 子类构造函数的原型对象指向父类的实例。
    • 优点: 简单易懂,易于实现。
    • 缺点:
      • 引用类型的属性会被所有实例共享,存在属性共享的问题。
      • 不能向父类传递参数, 无法通过子类向父类传参。
js 复制代码
function Parent() {
    this.name = '爸爸'
    this.hobbies = ['吃饭','睡觉'];
}

Parent.prototype.getHobbies = function() {
    console.log(this.name + '的爱好是' + this.hobbies.join('和'));
};

function Child(name) {
    this.name = name;
    this.hobbies = ['哼小曲'];
}

Child.prototype = new Parent();

var parent = new Parent();
parent.getHobbies(); //爸爸的爱好是吃饭和睡觉

var child = new Child('小白');
child.getHobbies(); //小白的爱好是哼小曲

问题1:引⽤类型的属性被所有实例共享,举个例⼦:

js 复制代码
function Parent() {
    this.name = '爸爸'
    this.hobbies = ['吃饭','睡觉'];
}

Parent.prototype.getHobbies = function() {
    console.log(this.name + '的爱好是' + this.hobbies.join('和'));
};

function Child(name) {
    this.name = name;
}

// 子类的原型指向父类的实例
Child.prototype = new Parent();

var child1 = new Child('小白');
var child2 = new Child('小布');

child1.getHobbies(); // 小白的爱好是吃饭和睡觉
child2.getHobbies(); // 小布的爱好是吃饭和睡觉

// 修改小白的爱好,小布的爱好也跟着变了
child1.hobbies.push('哼小曲');

child1.getHobbies(); // 小白的爱好是吃饭和睡觉和哼小曲
child2.getHobbies(); // 小布的爱好是吃饭和睡觉和哼小曲

问题2:不能向父类传递参数,举个例⼦:

js 复制代码
function Parent(name) {
    this.name = name || '爸爸';
}

Parent.prototype.sayHello = function() {
    console.log('你好, 我是' + this.name);
};

function Child() {
    // 子类构造函数中没有传递参数给父类
}

Child.prototype = new Parent();

var child1 = new Child('小布');
child1.sayHello(); // 你好, 我是爸爸

子类虽然传了名字,依然输出爸爸的名字,向父类传递参数失败

2. 构造函数继承(Constructor Inheritance):

  • 实现方式: 在子类构造函数中调用父类构造函数,并使用 callapply 方法绑定 this
    • 优点:

      • 避免了属性共享问题。
      • 可以向父类传递参数。
    • 缺点:

      • 不能继承父类原型上的方法。
      • 每次创建子类实例都会创建一份父类方法的副本,存在内存浪费。
js 复制代码
function Parent(name) {
    this.name = name || '爸爸';
    this.hobbies = ['吃饭','睡觉'];
}

function Child(name) {
    Parent.call(this, name);
}

var parent = new Parent();
console.log(parent.name + '的爱好是' + parent.hobbies.join('和')); // 爸爸的爱好是吃饭和睡觉
var child1 = new Child('小白');
child1.hobbies.push('哼小曲');
console.log(child1.name + '的爱好是' + child1.hobbies.join('和')); // 小白的爱好是吃饭和睡觉和哼小曲
var child2 = new Child('小布');
console.log(child2.name + '的爱好是' + child2.hobbies.join('和')); // 小布的爱好是吃饭和睡觉

问题1: 不能继承父类原型上的方法

js 复制代码
function Parent(name) {
    this.name = name || '爸爸';
    this.hobbies = ['吃饭','睡觉'];
}
Parent.prototype.sayHello = function() {
    console.log('你好, 我是' + this.name);
};

function Child(name) {
    Parent.call(this, name);
}

var parent = new Parent();
parent.sayHello() //你好, 我是爸爸
var child1 = new Child('小白');
child1.sayHello() // child1.sayHello is not a function

3. 组合继承(Combination Inheritance):

  • 实现方式: 同时使用原型链继承和构造函数继承。

  • 优点:

    • 解决了原型链继承和构造函数继承各自的缺点。
    • 既可以继承父类原型上的方法,又可以避免属性共享问题。
  • 缺点:

    • 子类原型链上存在两份相同的属性,存在内存浪费。
js 复制代码
function Parent(name) {
    this.name = name || '爸爸';
    this.hobbies = ['吃饭','睡觉'];
}

Parent.prototype.getHobbies = function() {
    console.log(this.name + '的爱好是' + this.hobbies.join('和'));
};

function Child (name) {
    Parent.call(this, name);
}

Child.prototype = new Parent();
Child.prototype.constructor = Child;

var child1 = new Child('小白');
child1.hobbies.push('哼小曲');
child1.getHobbies() //小白的爱好是吃饭和睡觉和哼小曲

var child2 = new Child('小布');
child2.getHobbies() //小布的爱好是吃饭和睡觉

融合原型链继承和构造函数的优点,是 JavaScript 中最常⽤的继承模式。

4. 原型式继承(Prototype Pattern):

  • 实现方式: 利用一个空的构造函数作为中介,创建一个对象并将该对象的原型指向某个对象。
  • 优点: 简单方便,可以创建多个对象,共享同一个原型。
  • 缺点:
    • 无法传递参数。
    • 引用类型的属性会被所有实例共享,存在属性共享的问题。
js 复制代码
function createObj(o) {
     function F(){}
     F.prototype = o;
     return new F();
}

var parent = {
    name: '爸爸',
    hobbies: ['吃饭','睡觉'],
    getHobbies: function() {
        console.log(this.name + '的爱好是' + this.hobbies.join('和'));
    }
};

var child1 = createObj(parent);
child1.name = '小白';
child1.hobbies.push('哼小曲');
child1.getHobbies(); // 小白的爱好是吃饭和睡觉和哼小曲

var child2 = createObj(parent);
child1.name = '小布';
child1.getHobbies(); // 小布的爱好是吃饭和睡觉和哼小曲

缺点:包含引⽤类型的属性值始终都会共享相应的值,这点跟原型链继承⼀样。

5. 寄生式继承(Parasitic Inheritance):

  • 实现方式: 在原型式继承的基础上,对对象进行扩展,并返回扩展后的对象。
  • 优点: 在不破坏原型链的情况下,对对象进行扩展。
  • 缺点: 无法传递参数,存在属性共享的问题。
js 复制代码
function createObj(original) {
    var clone = Object.create(original);
    clone.getHobbies = function() {
        console.log(this.name + '的爱好是' + this.hobbies.join('和'));
    };
    return clone;
}

var parent = {
    name: '爸爸',
    hobbies: ['吃饭','睡觉'],
};

var child1 = createObj(parent);
child1.name = '小白';
child1.hobbies.push('哼小曲');
child1.getHobbies(); // 小白的爱好是吃饭和睡觉和哼小曲

var child2 = createObj(parent);
child2.name = '小布';
child2.getHobbies(); // 小布的爱好是吃饭和睡觉和哼小曲

6. 寄生组合式继承(Parasitic Combination Inheritance):

  • 实现方式: 使用寄生式继承来继承父类原型,然后将结果指定给子类的原型。
  • 优点: 解决了组合继承中父类被调用两次构造函数的
js 复制代码
// 父类构造函数
function Parent(name) {
    this.name = name || '爸爸';
    this.hobbies = ['吃饭','睡觉'];
}

// 父类原型方法
Parent.prototype.getHobbies = function() {
    console.log(this.name + '的爱好是' + this.hobbies.join('和'));
};

// 子类构造函数
function Child (name) {
    Parent.call(this, name);
}

// 使用寄生方式继承父类的原型方法
function inheritPrototype(subType, superType) { 
    var prototype = Object.create(superType.prototype); 
    prototype.constructor = subType; 
    subType.prototype = prototype; 
}

// 寄生组合式继承 
inheritPrototype(Child, Parent);

// 实例化子类 
var child1 = new Child('小白');
child1.hobbies.push('哼小曲');
child1.getHobbies(); //小白的爱好是吃饭和睡觉和哼小曲

var child2 = new Child('小布');
child2.getHobbies(); // 小布的爱好是吃饭和睡觉
相关推荐
小飞猪Jay3 小时前
C++面试速通宝典——13
jvm·c++·面试
一路向前的月光3 小时前
Vue2中的监听和计算属性的区别
前端·javascript·vue.js
长路 ㅤ   3 小时前
vue-live2d看板娘集成方案设计使用教程
前端·javascript·vue.js·live2d
Fan_web3 小时前
jQuery——事件委托
开发语言·前端·javascript·css·jquery
Jiaberrr4 小时前
Element UI教程:如何将Radio单选框的圆框改为方框
前端·javascript·vue.js·ui·elementui
安冬的码畜日常6 小时前
【D3.js in Action 3 精译_029】3.5 给 D3 条形图加注图表标签(上)
开发语言·前端·javascript·信息可视化·数据可视化·d3.js
太阳花ˉ6 小时前
html+css+js实现step进度条效果
javascript·css·html
john_hjy7 小时前
11. 异步编程
运维·服务器·javascript
风清扬_jd7 小时前
Chromium 中JavaScript Fetch API接口c++代码实现(二)
javascript·c++·chrome