细说JavaScript里的类继承

744 阅读3分钟

「这是我参与11月更文挑战的第17天,活动详情查看:2021最后一次更文挑战

前言

👉 上期回顾:JavaScript里Class类的基本介绍


本节主要来说说类里的继承,让我们先从一个常见的示例开始

extends关键字

假如当前创建了一个动物类Animal

class Animal {
 constructor(name) {
    this.speed = 0;
    this.name = name;
  }
  run(speed) {
    this.speed = speed;
    console.log(`${this.name} 奔跑的速度是 ${this.speed}.`);
  }
  stop() {
    this.speed = 0;
    console.log(`${this.name} 停止奔跑`);
  }
}
let animal = new Animal("My animal");

然后现在又想另外创建一个小狗类Dog,因为小狗Dog动物Animal,所以,【小狗】应该是基于【动物】的,需要能够访问【动物】类的方法,使【小狗】可以做“一般”动物可以做的事情。

这个时候你就可以用到类继承了,

📖 关键字extends

📖 基本语法class Child extends Parent

现在来试试创建一个继承自【动物】的【小狗】

class Dog extends Animal {
  speak() {
    console.log(`${this.name} 在叫!`);
  }
}

🍉 运行结果:

image-20211118225333333

可以看到【小狗】的对象除了可以访问到自己的speak()方法,还可以访问到【动物】里的run() 和 stop()等方法。

💡 示例分析:

在内部,关键字 extends 使用了旧的原型机制进行工作。

它将 Dog.prototype.[[Prototype]] 设置为 Animal.prototype。所以,如果在 Dog.prototype 中找不到一个方法,JavaScript 就会从 Animal.prototype 中获取该方法。

传送门:原型的继承

重写父类的方法

现在我们在来思考一个问题,【动物】中有stop()方法,如果我在【小狗】里也写一个stop()方法,那么执行的是哪个呢?

🍉 运行结果:

image-20211118230230791

从运行结果可以看到,【自己】的方法执行的优先级比【父级】的更高,但是有的时候,我们只是希望在【父类】方法的基础上调整或者扩展,并不想完全替换掉【父类】的方法,那这要怎么实现呢?

Class 为此提供了 "super" 关键字。

  • 执行 super.method(...) 来调用一个父类方法。
  • 执行 super(...) 来调用一个父类 constructor(只能在我们的 constructor 中)。

现在我们再来试一试,让小狗在【停止奔跑】时,【叫】一声

class Dog extends Animal {
  stop() {
    super.stop(); // 调用父类的 stop
    console.log(`${this.name} 叫了一声!`);
  }
}

🍉 运行结果:

image-20211118230709753

重写constructor

最后在来看一个问题,可以发现我们上面的Dog都没有自己的constructor

根据 规范,如果一个类扩展了另一个类并且没有 constructor,那么将生成下面这样的“空” constructor

class Dog extends Animal {
  // 为没有自己的 constructor 的扩展类生成的
  constructor(...args) {
    super(...args);
  }
}

它调用了父类的 constructor,并传递了所有的参数。如果我们没有写自己的 constructor,就会出现这种情况。

现在我们给Dog添加一个自定义的constructor来看看

class Animal {
 constructor(name) {
    this.speed = 0;
    this.name = name;
  }
}
class Dog extends Animal {
 constructor(name, earLength) {
    this.speed = 0;
    this.name = name;
    this.earLength = earLength;
  }
}

let dog = new Dog("Dog", 10);

🍉 运行结果:

image-20211119202111190

可以看到,此时尝试新建一个dog时,报错了。

这是因为:**继承类的 constructor 必须调用 super(...),并且 一定要在使用 this 之前调用。**所以想让Dog的 constructor 正常工作,它需要在使用 this 之前调用 super()

🎨 示例:

...
class Dog extends Animal {
 constructor(name, earLength) {
    super(name);   //关键
    this.name = name;
    this.earLength = earLength;
  }
}
...

🍉 运行结果:

image-20211119202550978

参考资料:

MDN Classes

WIKI Class (computer programming)

Class inheritance


🎨【点赞】【关注】不迷路,更多前端干货等你解锁

往期推荐

👉 产品、技术、设计等各互联网领域的「基础术语」扫盲

👉 Web安全的防御手段都在这里了!

👉 JavaScript的7大类型补缺补漏!

👉 JavaScript深拷贝和浅拷贝看这篇就够了!