Class inheritance
JavaScript Class inheritance (JavaScript类继承)
In this chapter, we will explore the class inheritance. (在本章中,我们将探讨类继承。)
It is a way for one class to extend another one, and allows creating new functionality on top of the existing. (这是一个类扩展另一个类的一种方式,并允许在现有的基础上创建新功能。)
The “extend” Keyword
The “extend” Keyword (“EXTEND”关键词)
Imagine that you have a class Car:
class Car {
constructor(name) {
this.name = name;
this.speed = 0;
}
drive(speed) {
this.speed += speed;
console.log(`${this.name} drives with speed ${this.speed}.`);
}
stop() {
this.speed = 0;
console.log(`${this.name} stands still.`);
}
}
let car = new Car("My Car");
Now, let’s say that you intend to create a new class MyCar. (现在,假设您打算创建一个新的MyCar类。)
Since car are cars, the MyCar class can be based on Car having access to car methods so that they are able to do what generic cars can do. (由于汽车是汽车, MyCar类可以基于汽车访问汽车方法,以便他们能够做通用汽车可以做的事情。)
The syntax for extending another class is as follows:
class Child extends Parent
The next step is creating class class MyCar that inherits from Car:
class Car {
constructor(name) {
this.name = name;
this.speed = 0;
}
drive(speed) {
this.speed += speed;
console.log(`${this.name} drives with speed ${this.speed}.`);
}
stop() {
this.speed = 0;
console.log(`${this.name} stands still.`);
}
}
let car = new Car("My Car");
class MyCar extends Car {
parked() {
console.log(`${this.name} is parked!`);
}
}
let mycar = new MyCar("MyCar is Black");
mycar.drive(60); // MyCar drives with speed 60.
mycar.parked(); // MyCar is parked!
The object of Car class has access to both MyCar methods, like car.parked(), and Car methods, like mycar.drive(). (Car类的对象可以访问MyCar方法(如car.parked () )和Car方法(如mycar.drive () )。)
The extends keyword uses prototype mechanics. It is setting MyCar.prototype. In case a method can’t be found in .prototype, JavaScript takes it from Car.prototype. (Extends关键字使用原型机制。它正在设置MyCar.prototype。如果在.prototype中找不到方法, JavaScript将从Car.prototype获取该方法。)
For example, for finding mycar.drive, the engine checks out:
The mycar object ( doesn’t have drive) (- mycar对象(没有驱动器))
Its prototype (MyCar.prototype). It has parked but doesn’t have drive) (-其原型( MyCar.prototype )。它已停放但没有驱动器))
Its prototype, which is Car.prototype due to extends. Finally, it has the run method. (-它的原型, Car.prototype由于扩展。最后,它具有run方法。)
Overriding a Method
Overriding a Method (覆盖方法)
The next step is to override a method. All the methods not specified in class MyCar are taken directly “as is” from class Car. (下一步是覆盖方法。 类中未指定的所有方法 MyCar直接“按原样”从Class Car中取出。)
But you need to specify your own method in MyCar such as stop() to use it instead. (但是,您需要在MyCar中指定自己的方法,例如stop ()才能使用它。)
Let’s have a look at the example:
class MyCar extends Car {
stop() {
// ...now this will be used for mycar.stop()
(//...现在这将用于mycar.stop ())
// instead of stop() from class Car
(//而不是从类Car停止())
}
}
The aim is not replacing a parent method but building on top of it to tweak or extend its functionality. You do something in your method calling the parent method either before/after it or in the process. (目的不是替换父方法,而是在其上构建以调整或扩展其功能。您可以在方法中调用父方法之前/之后或过程中执行某些操作。)
There is a “super” keyword for that provided by classes:
super.method(…) for calling a parent method super(…) for calling a parent constructor (only inside your constructor). (super.method (…)用于调用父方法 super (…)用于调用父构造函数(仅在构造函数内部)。)
Overriding Constructor
Overriding Constructor (覆盖构造函数)
It’s a little tricky to override a constructor. (覆盖构造函数有点棘手。)
In the previous examples, MyCar didn’t have its constructor. (在前面的示例中, MyCar没有构造函数。)
In case a class extends another one and has no constructor, then an empty constructor is created, like this:
class MyCar extends Car {
// generated for extending classes without own constructors
(//在没有自己的构造函数的情况下为扩展类生成)
constructor(...args) {
super(...args);
}
}
Basically, it calls the parent constructor, passing it all the arguments. It happens in case you don’t write your own constructor. (基本上,它调用父构造函数,传递所有参数。如果您没有编写自己的构造函数,则会发生这种情况。)
Now, let’s practice adding a custom constructor to MyCar. It specifies the engineForce besides name:
class Car {
constructor(name) {
this.speed = 0;
this.name = name;
}
// ...
}
class MyCar extends Car {
constructor(name, engineForce) {
this.speed = 0;
this.name = name;
this.engineForce = engineForce;
}
// ...
}
// Doesn't work!
let mycar = new MyCar("MyCar is Black", 60); // Error: this is not defined.
As you can see, an error occurred not allowing to create rabbits. (如您所见,发生了不允许创建家兔的错误。)
To understand the reason, you need to get into details. (要了解原因,您需要深入了解详情。)
In JavaScript, a constructor function of an inheriting class (known as “derived constructor”) and other functions are separated. A derived constructor has a unique internal property [[ConstructorKind]]:“derived”. It’s a unique internal label.
The label’s behavior is affected by new. (标签的行为会受到NEW的影响。)
Whenever an ordinary function is executed with new, it generates an empty object assigning it to this. (-每当使用new执行普通函数时,它都会生成一个空对象,并将其分配给this。)
When a derived constructor runs, it doesn’t happen. It expects parent constructor to do that. (-派生构造函数运行时,不会发生这种情况。它希望父构造函数执行此操作。)
Hence, the derived constructor should call super to execute its parent constructor. In different circumstances, the object for this will not be generated. So, an error will occur. (因此,派生构造函数应调用super来执行其父构造函数。在不同的情况下,不会为此生成对象。因此,会发生错误。)
It is necessary to call super() before using this for the MyCar constructor to work. (在使用super ()使MyCar构造函数工作之前,必须调用super ()。)
Here is an example:
class Car {
constructor(name) {
this.speed = 0;
this.name = name;
}
// ...
}
class MyCar extends Car {
constructor(name, engineForce) {
super(name);
this.engineForce = engineForce;
}
// ...
}
// now fine
let mycar = new MyCar("MyCar is black", 60);
console.log(mycar.name); // MyCar is black
console.log(mycar.engineForce); // 60
[[HomeObject]]
[[HomeObject]]
In JavaScript, there is another unique internal property for functions. It’s [[HomeObject]]. (在JavaScript中,函数还有另一个唯一的内部属性。是[[HomeObject]]。)
Whenever a function is specified as an object method or a class, its [[HomeObject]] property is transformed into that object. (每当将函数指定为对象方法或类时,其[[HomeObject]]属性都会转换为该对象。)
Then super uses it for resolving the parent prototype along with its methods. (然后, super使用它来解析父原型及其方法。)
Let’s see how it works:
let car = {
name: "Car",
drive() { // car.drive.[[HomeObject]] == car
console.log(`${this.name} drives.`);
}
};
let mycar = {
__proto__: car,
name: "MyCar",
drive() { // mycar.drive.[[HomeObject]] == mycar
super.drive();
}
};
let powerfulEngine = {
__proto__: mycar,
name: "Machine with Powerful Engine ",
drive() { // powerfulEngine.drive.[[HomeObject]] == powerfulEngine
super.drive();
}
};
// works correctly
powerfulEngine.drive(); // drives.
It works thanks to the mechanics of [[HomeObject]]. The powerfulEngin e.drive method knows its [[HomeObject]] and takes the parent method from its prototype without using this. (这要归功于[[HomeObject]]的机制。powerfulEngin e.drive方法知道它的[[HomeObject]] ,并在不使用this的情况下从其原型中获取父方法。)
Methods, Not Function Properties
Methods, Not Function Properties (方法,非函数属性)
[[HomeObject]] is defined for methods in classes, as well as in plain objects. But note that for objects you must specify methods exactly as method() and not like “method: function()”. That is an essential difference for JavaScript.
A non-method syntax mentioned below is used for comparison. [[HomeObject]] is not applied, and the inheritance doesn’t operate:
let car = {
drive: function () { // intentionally writing like this instead of drive() {...
// ...
(//...)
}
};
let mycar = {
__proto__: car,
drive: function () {
super.drive();
}
};
mycar.drive(); // Error calling super (because there's no [[HomeObject]])