herencia de clase
La clase puede
extends
implementar la herencia a través de palabras clave, lo que permite que las subclases hereden las propiedades y métodos de la clase principal. La forma de escribir extensiones es mucho más clara y conveniente que la herencia de cadena prototipo de ES5.
// 继承Person类中的sayHi方法
class Person {
sayHi(){
console.log("hello");
}
}
class Chinese extends Person {}
// 继承Person类中的属性和方法
class Person {
constructor(name, age, gender){
this.name = name;
this.age = age;
this.gender = gender;
}
sayHi(){
console.log("hello");
}
}
// 子类
class Chinese extends Person {
constructor(name, age, gender, skin){
// 在子类中,必须在constructor函数中,首先调用super()
super(name, age, gender); // 相当于Person.call(this,name,age,gender)
// 调用super之后才可以去写其他代码
this.skin = skin;
}
}
let xm = new Chinese("xm", 20, "male", "黄");
console.log(xm);
xm.sayHi();
class Point {
}
class Student extends Point {
}
En el ejemplo anterior, Point
es la clase padre y Student
la subclase, la que hereda todas las propiedades y métodos de la clase a través de extends
palabras clave . Point
Pero como no se implementa ningún código, las dos clases son exactamente iguales, que es un duplicado de una Point
clase.
A continuación, Student
agregamos código dentro.
class Point { /* ... */ }
class Student extends Point {
constructor(x, y, color) {
super(x, y); // 调用父类的constructor(x, y)
this.color = color;
}
toString() {
return this.color + ' ' + super.toString(); // 调用父类的toString()
}
}
En el ejemplo anterior, las palabras clave aparecen tanto en los constructor()
métodos como en los métodos. Aquí, se representa el constructor de la clase principal, que se utiliza para crear un objeto de instancia de la clase principal.toString()
super
super
ES6 estipula que las subclases deben llamarse en constructor()
los métodos super()
, de lo contrario, se informará un error. Esto se this
debe a que el constructor de la superclase primero debe dar forma al objeto de la subclase para obtener los mismos atributos y métodos de instancia que la superclase, y luego procesarlo para agregar los atributos y métodos de instancia propios de la subclase. Si no llama super()
al método, la subclase no obtendrá su propio this
objeto.
class Point { /* ... */ }
class Student extends Point {
constructor() {
}
}
let cp = new Student(); // ReferenceError
En el código anterior,
Student
la clase principal se heredaPoint
, pero no se llama a su constructorsuper()
, lo que genera un error al crear una nueva instancia.¿Por qué se debe llamar al constructor de la subclase
super()
? La razón es que el mecanismo de herencia de ES6 es completamente diferente al de ES5. El mecanismo de herencia de ES5 consiste en crear primero un objeto de instancia de una subclase independiente y luego agregar el método de la clase principal a este objeto, es decir, "primero la instancia, la herencia después". El mecanismo de herencia de ES6 es agregar primero las propiedades y métodos de la clase principal a un objeto vacío y luego usar el objeto como una instancia de la subclase, es decir, "herencia primero, instancia última". Esta es la razón por la cual la herencia ES6 tiene que llamarsuper()
primero al método, porque este paso generará unthis
objeto que hereda la clase principal, y la clase principal no se puede heredar sin este paso.Tenga en cuenta que esto significa que cuando se crea una nueva instancia de una subclase, el constructor de la superclase debe ejecutarse una vez.
class Foo {
constructor() {
console.log(1);
}
}
class Bar extends Foo {
constructor() {
super();
console.log(2);
}
}
const bar = new Bar();
// 1
// 2
En el ejemplo anterior, cuando se crea una nueva instancia de la subclase Bar, se generarán 1 y 2. La razón es que cuando se llama al constructor de la subclase
super()
, el constructor de la clase padre se ejecutará una vez.Otro punto a tener en cuenta es que en el constructor de la subclase, la palabra clave
super()
solo se puede usar después de la llamadathis
, de lo contrario, se informará un error. Esto se debe a que la construcción de una instancia de subclase debe completar primero la herencia de la clase principal, y solo lossuper()
métodos pueden permitir que la instancia de la subclase herede la clase principal.
class Point {
constructor(x, y) {
this.x = x;
this.y = y;
}
}
class ColorPoint extends Point {
constructor(x, y, color) {
this.color = color; // ReferenceError
super(x, y);
this.color = color; // 正确
}
}
En el código anterior, la palabra clave se usa antes de
constructor()
llamar al método de la subclase y el resultado es un error, pero es correcto después de que se coloca.super()
this
super()
Si la subclase no define un
constructor()
método, este método se agregará de manera predeterminada y se llamará dentro desuper()
. Es decir, cualquier subclase tieneconstructor()
métodos, ya sea explícitamente definidos o no.
class ColorPoint extends Point {
}
// 等同于
class ColorPoint extends Point {
constructor(...args) {
super(...args);
}
}
Con la definición de la subclase, puede generar una instancia de la subclase.
let cp = new ColorPoint(25, 8, 'green');
cp instanceof ColorPoint // true
cp instanceof Point // true
En el ejemplo anterior, el objeto de instancia
cp
es una instanciaColorPoint
dePoint
ambas clases al mismo tiempo, que es exactamente el mismo comportamiento que ES5.A excepción de las propiedades privadas, todas las propiedades y métodos de la clase principal serán heredados por la subclase, incluidos los métodos estáticos.
class A {
static hello() {
console.log('hello world');
}
}
class B extends A {
}
B.hello() // hello world
En el código anterior,
hello()
esA
el método estático de la clase, el métodoB
heredadoA
y tambiénA
el método estático heredado.Las subclases no pueden heredar las propiedades privadas de la clase principal, o en otras palabras, las propiedades privadas solo se pueden usar en la clase en la que están definidas.
class Foo {
#p = 1;
#m() {
console.log('hello');
}
}
class Bar extends Foo {
constructor() {
super();
console.log(this.#p); // 报错
this.#m(); // 报错
}
}
En el ejemplo anterior, si la subclase Bar llama a la propiedad privada o al método privado de la clase principal Foo, se informará un error.
Si la clase principal define métodos de lectura y escritura para propiedades privadas, las subclases pueden usar estos métodos para leer y escribir propiedades privadas.
class Foo {
#p = 1;
getP() {
return this.#p;
}
}
class Bar extends Foo {
constructor() {
super();
console.log(this.getP()); // 1
}
}
En el ejemplo anterior,
getP()
es el método utilizado por la clase principal para leer las propiedades privadas. A través de este método, la subclase puede leer las propiedades privadas de la clase principal.
súper palabra clave
super
Esta palabra clave se puede utilizar como función y como objeto. Su uso es completamente diferente en ambos casos.El primer caso,
super
cuando se llama como función, representa el constructor de la clase principal. ES6 requiere que el constructor de una subclase ejecute unasuper
función una vez.
class A {}
class B extends A {
constructor() {
super();
}
}
En el código anterior,
B
entre los constructores de la subclase,super()
se llama al constructor de la clase principal. Esto es obligatorio, de lo contrario, el motor de JavaScript arrojará un error.Tenga en cuenta que
super
aunque representa el constructor de la clase padre , devuelve una instancia de laA
subclase , es decir, la interna se refiere a la instancia, por lo que aquí es equivalente .B
super
this
B
super()
A.prototype.constructor.call(this)
class A {
constructor() {
console.log(new.target.name);
}
}
class B extends A {
constructor() {
super();
}
}
new A() // A
new B() // B
En el código anterior,
new.target
apunta a la función que se está ejecutando actualmente. Como puede ver, en elsuper()
momento de la ejecución, apunta al constructor de la subclaseB
, no al constructor de la clase principalA
. Es decir,super()
el punto internothis
esB
.Cuando se usa como una función,
super()
solo se puede usar en el constructorconstructor
de y se informará un error en otros lugares.
class A {}
class B extends A {
m() {
super(); // 报错
}
}
En el código anterior, cuando se
super()
usa enB
el método de la clasem
, provocará un error de sintaxis.En el segundo caso,
super
como objeto, en los métodos ordinarios apunta al objeto prototipo de la clase padre; en los métodos estáticos, apunta a la clase padre.
class A {
p() {
return 2;
}
}
class B extends A {
constructor() {
super();
console.log(super.p()); // 2
}
}
let b = new B();
En el código anterior, la subclase
B
sesuper.p()
usasuper
como un objeto. En este momento,super
en el método ordinario, apunte aA.prototype
, por losuper.p()
que es equivalente aA.prototype.p()
.Cabe señalar aquí que debido a que
super
apunta al objeto prototipo de la clase principal, los métodos o propiedades definidos en la instancia de la clase principal no se puedensuper
llamar.
class A {
constructor() {
this.p = 2;
}
}
class B extends A {
get m() {
return super.p;
}
}
let b = new B();
b.m // undefined
En el código anterior,
p
es un atributo de laA
instancia de la clase principal,super.p
por lo que no se puede hacer referencia a él.Si el atributo está definido en el objeto prototipo de la clase principal,
super
se puede obtener.
class A {}
A.prototype.x = 2;
class B extends A {
constructor() {
super();
console.log(super.x) // 2
}
}
let b = new B();
En el código anterior, la propiedad
x
se defineA.prototype
arriba, porsuper.x
lo que se puede obtener su valor.ES6 estipula que cuando
super
se llama al método de la clase principal en el método ordinario de la subclase, la parte interna del métodothis
apunta a la instancia de la subclase actual.
class A {
constructor() {
this.x = 1;
}
print() {
console.log(this.x);
}
}
class B extends A {
constructor() {
super();
this.x = 2;
}
m() {
super.print();
}
}
let b = new B();
b.m() // 2
En el código anterior,
super.print()
aunque la llamada es síA.prototype.print()
, la instanciaA.prototype.print()
internathis
apunta a la subclase , lo que da como resultado el resultado sí , no . Es decir, lo que realmente se ejecuta es .B
2
1
super.print.call(this)
Dado que
this
apunta a una instancia de subclase, si asuper
un atributosuper
se le asigna un valor,this
el atributo asignado se convertirá en el atributo de la instancia de subclase.
class A {
constructor() {
this.x = 1;
}
}
class B extends A {
constructor() {
super();
this.x = 2;
super.x = 3;
console.log(super.x); // undefined
console.log(this.x); // 3
}
}
let b = new B();
En el código anterior, la
super.x
asignación es3
, en este momento, es equivalente athis.x
asignar a3
. Y cuando se leesuper.x
, se leeA.prototype.x
, así que se regresaundefined
.Si se
super
usa como un objeto en un método estático,super
apuntará a la clase principal en lugar del objeto prototipo de la clase principal.
class Parent {
static myMethod(msg) {
console.log('static', msg);
}
myMethod(msg) {
console.log('instance', msg);
}
}
class Child extends Parent {
static myMethod(msg) {
super.myMethod(msg);
}
myMethod(msg) {
super.myMethod(msg);
}
}
Child.myMethod(1); // static 1
var child = new Child();
child.myMethod(2); // instance 2
En el código anterior,
super
el método estático apunta a la clase principal y el método normal apunta al objeto prototipo de la clase principal.
super
Además, al llamar al método de la clase principal en el método estático de la subclase, el método internothis
apunta a la subclase actual, no a la instancia de la subclase.
class A {
constructor() {
this.x = 1;
}
static print() {
console.log(this.x);
}
}
class B extends A {
constructor() {
super();
this.x = 2;
}
static m() {
super.print();
}
}
B.x = 3;
B.m() // 3
En el código anterior,
B.m
dentro del método estático,super.print
apunta al método estático de la clase principal.this
El puntero dentro de este método esB
, noB
la instancia.Tenga en cuenta que al usarlo
super
, debe especificar explícitamente si lo usará como una función o como un objeto, de lo contrario, se informará un error.
class A {}
class B extends A {
constructor() {
super();
console.log(super); // 报错
}
}
En el código anterior,
console.log(super)
essuper
imposible saber si se usa como una función o como un objeto, por lo que se informará un error cuando el motor de JavaScript analice el código. En este momento, si el tipo de datos se puede indicar claramentesuper
, no se informará ningún error.
class A {}
class B extends A {
constructor() {
super();
console.log(super.valueOf() instanceof B); // true
}
}
let b = new B();
En el código anterior,
super.valueOf()
indica quesuper
es un objeto, por lo que no se informará ningún error. Al mismo tiempo, dado que se hacesuper
la instancia a lathis
que apuntaB
,super.valueOf()
devuelve unaB
instancia de .
super
Finalmente, dado que los objetos siempre heredan de otros objetos, puede usar palabras clave en cualquier objeto .
var obj = {
toString() {
return "MyObject: " + super.toString();
}
};
obj.toString(); // MyObject: [object Object]