web-dev-qa-db-fra.com

Appeler des méthodes statiques à partir de méthodes de classe ES6 standard

Quelle est la méthode standard pour appeler des méthodes statiques? Je peux penser à utiliser constructor ou à utiliser le nom de la classe elle-même. Je n'aime pas ce dernier car il ne me semble pas nécessaire. Le premier est-il le moyen recommandé ou y at-il autre chose?

Voici un exemple (artificiel):

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

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

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

Les deux méthodes sont viables, mais elles font des choses différentes en matière d'héritage avec une méthode statique surchargée. Choisissez celui dont vous attendez le comportement:

_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
_

Faire référence à la propriété statique via la classe sera réellement statique et donnera constamment la même valeur. Utiliser _this.constructor_ à la place utilisera la répartition dynamique et fera référence à la classe de l'instance actuelle, où la propriété statique pourrait avoir la valeur héritée, mais pourrait également être remplacé.

Cela correspond au comportement de Python, dans lequel vous pouvez choisir de faire référence à des propriétés statiques via le nom de la classe ou l'instance self.

Si vous vous attendez à ce que les propriétés statiques ne soient pas remplacées (et faites toujours référence à celle de la classe actuelle), comme en Java , utilisez la référence explicite.

191
Bergi

Je suis tombé sur ce fil en cherchant une réponse à un cas similaire. En gros, toutes les réponses sont trouvées, mais il est toujours difficile d’en extraire l’essentiel.

Types d'accès

Supposons une classe Foo probablement dérivée d'une autre classe avec probablement plus de classes dérivées de celle-ci.

Puis accéder

  • de static méthode/getter de Foo
    • certains probablement statique remplacée méthode/getter:
      • this.method()
      • this.property
    • certains probablement instance surchargée méthode/getter:
      • impossible par conception
    • own statique non substituée méthode/getter:
      • Foo.method()
      • Foo.property
    • own instance non remplacée méthode/getter:
      • impossible par conception
  • de instance méthode/getter de Foo
    • certains probablement statique remplacée méthode/getter:
      • this.constructor.method()
      • this.constructor.property
    • certains probablement instance surchargée méthode/getter:
      • this.method()
      • this.property
    • own statique non substituée méthode/getter:
      • Foo.method()
      • Foo.property
    • own instance non remplacée méthode/getter:
      • impossible par intention, à moins d'utiliser une solution de contournement :
        • Foo.prototype.method.call( this )
        • Object.getOwnPropertyDescriptor( Foo.prototype,"property" ).get.call(this);

N'oubliez pas que l'utilisation de this ne fonctionne pas de cette manière lorsque vous utilisez des fonctions de flèche ou invoquez des méthodes/accesseurs liés explicitement à une valeur personnalisée.

Contexte

  • Quand dans le contexte de la méthode d'une instance ou d'un getter
    • this fait référence à l'instance actuelle.
    • super fait essentiellement référence à la même instance, mais des méthodes d'adressage et des accesseurs écrits dans le contexte d'une classe courante sont en train de s'étendre (en utilisant le prototype du prototype de Foo).
    • la définition de la classe d'instance utilisée lors de sa création est disponible selon this.constructor.
  • Lorsque vous êtes dans le contexte d’une méthode statique ou d’un getter, il n’existe aucune "instance actuelle" par intention, et ainsi de suite.
    • this est disponible pour se référer directement à la définition de la classe actuelle.
    • super ne fait pas non plus référence à une instance, mais à des méthodes statiques et à des getters écrits dans le contexte d'une classe de classe en cours d'extension.

Conclusion

Essayez ce code:

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 );
60
Thomas Urban

Si vous envisagez de faire n'importe quel type d'héritage, je vous recommanderais donc this.constructor. Cet exemple simple devrait illustrer pourquoi:

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() enregistrera ConstructorSuper Hello ConstructorSuper! sur la console
  • test2.callPrint() enregistrera ConstructorSub Hello ConstructorSub! sur la console

La classe nommée ne traitera pas bien l'héritage à moins que vous ne redéfinissiez explicitement chaque fonction qui fait référence à la classe nommée. Voici un exemple:

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() enregistrera NamedSuper Hello NamedSuper! sur la console
  • test4.callPrint() enregistrera NamedSuper Hello NamedSub! sur la console

Voir tout ce qui précède dans Babel REPL .

Vous pouvez voir que test4 pense toujours que c'est dans la super classe; dans cet exemple, cela peut ne pas sembler une grosse affaire, mais si vous essayez de référencer des fonctions membres qui ont été remplacées ou de nouvelles variables membres, vous aurez des problèmes.

17
Andrew Odri