Búsqueda de sitios web

javascript - Llamar a métodos estáticos desde métodos regulares de clases ES6


¿Cuál es la forma estándar de llamar a los métodos estáticos? Puedo pensar en usar constructor o en usar el nombre de la propia clase, no me gusta esta última ya que no parece necesaria. ¿Es la primera la forma recomendada, o hay otra manera?

Aquí hay un ejemplo (forzado):

class SomeObject {
  constructor(n){
    this.n = n;
  }

  static print(n){
    console.log(n);
  }

  printN(){
    this.constructor.print(this.n);
  }
}

Respuesta #1

Ambos métodos son viables, pero hacen cosas diferentes en lo que respecta a la herencia con un método estático sobrescrito. Elige el que tenga el comportamiento que esperas:

class Super {
  static whoami() {
    return "Super";
  }
  lognameA() {
    console.log(Super.whoami());
  }
  lognameB() {
    console.log(this.constructor.whoami());
  }
}
class Sub extends Super {
  static whoami() {
    return "Sub";
  }
}
new Sub().lognameA(); // Super
new Sub().lognameB(); // Sub

Referirse a la propiedad estática a través de la clase será efectivamente estático y dará constantemente el mismo valor. Usar this.constructor en su lugar utilizará despacho dinámico y se referirá a la clase de la instancia actual, donde la propiedad estática podría tener el valor heredado pero también podría ser sobrescrita.

Esto coincide con el comportamiento de Python, donde puedes elegir referirte a las propiedades estáticas ya sea mediante el nombre de la clase o la instancia self.

Si esperas que las propiedades estáticas no sean sobrescritas (y siempre se refieran a la de la clase actual), como en Java, usa la referencia explícita.

Respuesta #2

Me tropecé con este hilo buscando una respuesta a un caso similar. Básicamente, se encuentran todas las respuestas, pero todavía es difícil extraer lo esencial de ellas.

Tipos de acceso

Asume una clase Foo probablemente derivada de alguna otra clase(s) con probablemente más clases derivadas de ella.

Luego accediendo:

  • desde el método/getter static de la clase Foo:

    • algunos probablemente método/getter static sobrescrito:

      • this.method()
      • algunos probablemente sobrescritos static método/getter:
      • this.method()

      • this.property
    • algunos probablemente método/obtenedor de instancia sobrescrito:

      • imposible por diseño
    • propio método/getter estático no sobrescrito:

      • Foo.method()
      • Foo.property
    • propio método/getter de instancia no sobrescrito:

      • imposible por diseño
  • del método/propiedad de instancia de la clase Foo:

    • algunos probablemente método/getter static sobrescrito:

      • this.constructor.method()
      • algunos probablemente sobrescribieron static método/getter:
      • this.constructor.method()

      • this.constructor.property
    • algunos probablemente método/obtenedor de instancia sobrescrito:

      • this.method()
      • this.property
    • propio método/getter static no sobrescrito:

      • Foo.method()
      • Foo.property
    • propio método/getter de instancia no sobrescrito:

      • no es posible por intención a menos que se utilice algún método alternativo:

        • Foo.prototype.method.call( this )
        • no es posible por intención a menos que se use algún método alternativo:
        • Foo.prototype.method.call( this )

        • Object.getOwnPropertyDescriptor(Foo.prototype,'<property>' ).get.call(this)

Ten en cuenta que usar this no funciona de esta manera al usar funciones flecha functions o al invocar métodos/getters explícitamente vinculados a un valor personalizado.

Fondo

  • Cuando está en el contexto del método/getter de una instancia

    • this se refiere a la instancia actual.
    • super básicamente se refiere a la misma instancia, pero de alguna manera está accediendo a métodos/getters escritos en el contexto de alguna class que la actual está extendiendo (usando el prototipo del prototipo de Foo).
    • La definición de la class de una instancia utilizada al crearla está disponible a través de this.constructor.
  • Cuando se está en el contexto de un método/getter static, no hay "instancia actual" por intención y por lo tanto:

    • this está disponible para referirse directamente a la definición de la clase actual.
    • super no se refiere a alguna instancia tampoco, sino a métodos/getters static escritos en el contexto de alguna clase que la actual está extendiendo.

Conclusión

Prueba este código:

class A {
  constructor( input ) {
    this.loose = this.constructor.getResult( input );
    this.tight = A.getResult( input );
    console.log( this.scaledProperty, Object.getOwnPropertyDescriptor( A.prototype, "scaledProperty" ).get.call( this ) );
  }

  get scaledProperty() {
    return parseInt( this.loose ) * 100;
  }
  
  static getResult( input ) {
    return input * this.scale;
  }
  
  static get scale() {
    return 2;
  }
}

class B extends A {
  constructor( input ) {
    super( input );
    this.tight = B.getResult( input ) + " (of B)";
  }
  
  get scaledProperty() {
    return parseInt( this.loose ) * 10000;
  }

  static get scale() {
    return 4;
  }
}

class C extends B {
  constructor( input ) {
    super( input );
  }
  
  static get scale() {
    return 5;
  }
}

class D extends C {
  constructor( input ) {
    super( input );
  }
  
  static getResult( input ) {
    return super.getResult( input ) + " (overridden)";
  }
  
  static get scale() {
    return 10;
  }
}


let instanceA = new A( 4 );
console.log( "A.loose", instanceA.loose );
console.log( "A.tight", instanceA.tight );

let instanceB = new B( 4 );
console.log( "B.loose", instanceB.loose );
console.log( "B.tight", instanceB.tight );

let instanceC = new C( 4 );
console.log( "C.loose", instanceC.loose );
console.log( "C.tight", instanceC.tight );

let instanceD = new D( 4 );
console.log( "D.loose", instanceD.loose );
console.log( "D.tight", instanceD.tight );

Respuesta #3

Si planeas hacer algún tipo de herencia, entonces recomendaría this.constructor. Este simple ejemplo debería ilustrar por qué:

class ConstructorSuper {
  constructor(n){
    this.n = n;
  }

  static print(n){
    console.log(this.name, n);
  }

  callPrint(){
    this.constructor.print(this.n);
  }
}

class ConstructorSub extends ConstructorSuper {
  constructor(n){
    this.n = n;
  }
}

let test1 = new ConstructorSuper("Hello ConstructorSuper!");
console.log(test1.callPrint());

let test2 = new ConstructorSub("Hello ConstructorSub!");
console.log(test2.callPrint());
  • test1.callPrint() registrará ConstructorSuper Hello ConstructorSuper! en la consola
  • test2.callPrint() registrará ConstructorSub Hello ConstructorSub! en la consola

La clase nombrada no manejará la herencia correctamente a menos que redefinas explícitamente cada función que haga referencia a la clase nombrada. Aquí hay un ejemplo:

class NamedSuper {
  constructor(n){
    this.n = n;
  }

  static print(n){
    console.log(NamedSuper.name, n);
  }

  callPrint(){
    NamedSuper.print(this.n);
  }
}

class NamedSub extends NamedSuper {
  constructor(n){
    this.n = n;
  }
}

let test3 = new NamedSuper("Hello NamedSuper!");
console.log(test3.callPrint());

let test4 = new NamedSub("Hello NamedSub!");
console.log(test4.callPrint());
  • test3.callPrint() registrará NamedSuper Hello NamedSuper! en la consola
  • test4.callPrint() registrará NamedSuper Hello NamedSub! en la consola

Ve todo lo anterior ejecutándose en Babel REPL.

Puedes ver a partir de esto que test4 todavía piensa que está en la superclase; en este ejemplo puede que no parezca un gran problema, pero si intentas hacer referencia a funciones miembro que han sido sobrescritas o a nuevas variables miembro, te encontrarás con problemas.

Artículos relacionados