JavaScript的6种常用继承方式

96 阅读4分钟

「回顾2022,展望2023,我正在参与2022年终总结征文大赛活动

1. 前言

首先我们要知道继承是一种代码重用的形式JavaScript又是一门基于原型的语言,这就意味着对象直接从其他对象继承。为了显著的减少软件开发的成本,提高代码的重用性,JavaScript的继承方式也是我们要学会的知识。那下面我就来介绍JavaScript 6种常用的继承方式。

2. 原型链继承

原型链继承就是让一个构造函数的原型是继承于另一个构造函数的实例,这样这个构造函数new出来的实例对象就会有另一个构造函数的属性。

SuperType.prototype.getSuperValue = function() {
    return this.property
  }
  function SuperType(t) {
    this.property = t
    this.like = {
      sport: 'running',
      book: 'js'
    }
    this.age = 18
  }
  Type.prototype = new SuperType()
  function Type(t) {
    this.typeproperty = t
  }
  var instance1 = new Type(false)
  instance1.age = 20

  console.log(instance1);

图片.png

如图所示,我们有一个SuperType构造函数,然后又创建了Type构造函数,同时让Type的原型赋值为SuperType new出来的的实例对象,这样我们用Typenew出来的实例对象原型上就可访问到SuperType的属性。这也就是原型链继承。

  • 优点:写法简单,容易理解
  • 缺点:
  • 1.会在子类实例上共享父类所有的引用类型实例属性
  • 2.不能给父类构造函数传参

3. 经典继承(伪造对象)

经典继承就是在子类的构造函数内部调用父类构造函数,再使用call()方法将父类的构造函数绑定到子类上。

SuperType.prototype.val = 123
function SuperType() {
  this.colors = ['red', 'blue', 'green']
}
function Type(t) {
  SuperType.call(this, t)
}
var instance1 = new Type()
instance1.colors.push('pink')
console.log(instance1);
var instance2 = new Type()
console.log(instance2);

图片.png

这样我们就是在子类构造函数Type继承到父类SuperTypecolor属性。

  • 优点 -解决了原型链不能传参的问题
  • 缺点
  • 1.方法都在父构造函数中定义,无法实现函数复用
  • 2.子类继承不到父类原型上的属性

4. 组合继承(伪经典继承)

组合继承就是将原型链继承和经典继承组合结合起来,使用原型链实现原型属性的继承,使用构造函数来实现对实例对象的继承。同时我们还可以实现函数的复用。

SuperType.prototype.sayName = function() {
    console.log(this.name);
  }
  function SuperType(name) {
    this.name = name
    this.colors = ['red', 'blue', 'green']
  }
  function Type(age, name) {
    this.age = age
    SuperType.call(this, name)
  }
  Type.prototype = new SuperType()
  Type.prototype.constructor = Type
  Type.prototype.sayAge = function() {
    console.log(this.age);
  }
  var instance1 = new Type(18, 'zhangshan')
  instance1.sayName()
  instance1.sayAge()
  console.log(instance1);

图片.png

我们通过了原型链继承拿到了父类的原型上的属性,又通过经典继承拿到了父类实例对象的属性。

  • 优点:
  • 解决了原型链继承和经典继承的缺陷。
  • 缺点:重复调用了两次父类的构造函数。一次是在创建子类原型时,另一次在子类构造函数内部

5. 原型式继承

原型式继承是借用一个函数传入一个对象作为参数,再在其内部创建一个临时的构造函数,然后将参数作为这个构造函数的原型,最后返回出这个构造函数的实例对象。

function object(o) {
    function F() {}
    F.prototype = o
    return new F()
  }
  var person = {
    name: '张三',
    age: 18,
    like: {
      sport: 'running'
    }
  }
  var obj = object(person)
  obj.like.sport = 'singing'
  console.log(obj);

图片.png

这样,我们obj对象就继承到了person身上的属性。

  • 优点:
  • 不需要单独创建构造函数
  • 缺点:
  • 属性中包含的引用值始终会在相关对象间共享,同时子类实例不能向父类传参

6. 寄生式继承

寄生式继承是创建一个仅用于封装继承过程的函数,该函数在内部以某种方式来增强对象,最后再返回对象,优缺点和原型式继承相同。

function createPerson(original) {
    var clone = Object.create(original)
    clone.say = function() { // 增强这个对象
      console.log('hello');
    }
    return clone
  }

  let obj = createPerson({'name':'zhangsan'})
  obj.say()
  console.log(obj);

图片.png

7. 寄生组合式继承

寄生组合式继承是通过借用构造函数来继承属性,通过原型链的混成形式来继承方法。本质上就是使用寄生式继承来继承父类的原型,然后再将结果指定给子类型的原型

SuperType.prototype.sayName = function() {
    console.log(this.name);
  }
  SuperType.prototype.like = {
    a: 1,
    b: 2
  }
  function SuperType(name) {
    this.name = name
    this.colors = ['red', 'blue', 'green']
  }
  function Type(name, age) {
    this.age = age
    SuperType.call(this, name)
  }
  var anotherPrototype = Object.assign(Type.prototype, SuperType.prototype)
  Type.prototype = anotherPrototype // new SuperType()
  var instance1 = new Type('Tom', 12)
  // instance1.colors.push('pink')
  instance1.like.a = 11
  instance1.sayName()
  console.log(instance1);

图片.png

  • 优点:
  • 只调用了一次父类构造函数,并且避免了子类原型上创建多余的属性。还可以保持原型链不变。(继承最优解
  • 缺点:
  • 代码复杂

8. 结语

除了这些继承方式,我们还可以用ES6中的class类实现继承。学习阶段,欢迎指教,点个赞呗。