[ES6] - herencia de clase

 herencia de clase

La clase puede extendsimplementar 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, Pointes la clase padre y Studentla subclase, la que hereda todas las propiedades y métodos de la clase a través de extendspalabras clave . PointPero como no se implementa ningún código, las dos clases son exactamente iguales, que es un duplicado de una Pointclase.

A continuación, Studentagregamos 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()supersuper

ES6 estipula que las subclases deben llamarse en constructor()los métodos super(), de lo contrario, se informará un error. Esto se thisdebe 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 thisobjeto.

class Point { /* ... */ }
class Student extends Point {
  constructor() {
  }
}
let cp = new Student(); // ReferenceError

En el código anterior, Studentla clase principal se hereda Point, pero no se llama a su constructor super(), 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 llamar super()primero al método, porque este paso generará un thisobjeto 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 llamada this, 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 los super()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()thissuper()

Si la subclase no define un constructor()método, este método se agregará de manera predeterminada y se llamará dentro de super(). Es decir, cualquier subclase tiene constructor()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 cpes una instancia ColorPointde Pointambas 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()es Ael método estático de la clase, el método Bheredado Ay también Ael 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 

superEsta palabra clave se puede utilizar como función y como objeto. Su uso es completamente diferente en ambos casos.

El primer caso, supercuando se llama como función, representa el constructor de la clase principal. ES6 requiere que el constructor de una subclase ejecute una superfunción una vez.

class A {}
class B extends A {
  constructor() {
    super();
  }
}

En el código anterior, Bentre 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 superaunque representa el constructor de la clase padre , devuelve una instancia de la Asubclase , es decir, la interna se refiere a la instancia, por lo que aquí es equivalente .BsuperthisBsuper()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.targetapunta a la función que se está ejecutando actualmente. Como puede ver, en el super()momento de la ejecución, apunta al constructor de la subclase B, no al constructor de la clase principal A. Es decir, super()el punto interno thises B.

Cuando se usa como una función, super()solo se puede usar en el constructor constructorde 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 en Bel método de la clase m, provocará un error de sintaxis.

En el segundo caso, supercomo 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 Bse super.p()usa supercomo un objeto. En este momento, superen el método ordinario, apunte a A.prototype, por lo super.p()que es equivalente a A.prototype.p().

Cabe señalar aquí que debido a que superapunta al objeto prototipo de la clase principal, los métodos o propiedades definidos en la instancia de la clase principal no se pueden superllamar.

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, pes un atributo de la Ainstancia de la clase principal, super.ppor lo que no se puede hacer referencia a él.

Si el atributo está definido en el objeto prototipo de la clase principal, superse 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 xse define A.prototypearriba, por super.xlo que se puede obtener su valor.

ES6 estipula que cuando superse llama al método de la clase principal en el método ordinario de la subclase, la parte interna del método thisapunta 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 instancia A.prototype.print()interna thisapunta a la subclase , lo que da como resultado el resultado sí , no . Es decir, lo que realmente se ejecuta es .B21super.print.call(this)

Dado que thisapunta a una instancia de subclase, si a superun atributo superse le asigna un valor, thisel 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.xasignación es 3, en este momento, es equivalente a this.xasignar a 3. Y cuando se lee super.x, se lee A.prototype.x, así que se regresa undefined.

Si se superusa como un objeto en un método estático, superapuntará 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, superel método estático apunta a la clase principal y el método normal apunta al objeto prototipo de la clase principal.

superAdemás, al llamar al método de la clase principal en el método estático de la subclase, el método interno thisapunta 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.mdentro del método estático, super.printapunta al método estático de la clase principal. thisEl puntero dentro de este método es B, no Bla 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)es superimposible 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 claramente super, 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 que superes un objeto, por lo que no se informará ningún error. Al mismo tiempo, dado que se hace superla instancia a la thisque apunta B, super.valueOf()devuelve una Binstancia de .

superFinalmente, 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]

Supongo que te gusta

Origin blog.csdn.net/m0_55960697/article/details/124086268
Recomendado
Clasificación