Chiama metodi statici dai normali metodi di class ES6

Qual è il modo standard per chiamare i metodi statici? Posso pensare di usare il constructor o usare il nome della class stessa, non mi piace quest’ultimo dato che non sembra necessario. Il primo è il modo raccomandato, o c’è qualcos’altro?

Ecco un esempio (forzato):

 class SomeObject { constructor(n){ this.n = n; } static print(n){ console.log(n); } printN(){ this.constructor.print(this.n); } } 

Entrambi i modi sono fattibili, ma fanno cose diverse quando si tratta di ereditare con un metodo statico sovrascritto. Scegli quello il cui comportamento ti aspetti:

 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 

Il riferimento alla proprietà statica tramite la class sarà in effetti statico e fornirà costantemente lo stesso valore. L’uso di this.constructor utilizzerà invece il dispatch dinamico e farà riferimento alla class dell’istanza corrente, in cui la proprietà statica potrebbe avere il valore ereditato ma potrebbe anche essere ignorata.

Corrisponde al comportamento di Python, in cui è ansible scegliere di fare riferimento alle proprietà statiche tramite il nome della class o l’ self un’istanza.

Se si prevede che le proprietà statiche non debbano essere sovrascritte (e fare sempre riferimento a quella della class corrente), come in Java , utilizzare il riferimento esplicito.

Mi sono imbattuto in questo thread alla ricerca di una risposta a un caso simile. Fondamentalmente tutte le risposte sono state trovate, ma è ancora difficile estrarre l’essenziale da esse.

Tipi di accesso

Assumiamo una class Foo probabilmente derivata da qualche altra class (o classi) con probabilmente più classi derivate da essa.

Quindi accedendo

  • dal metodo statico / getter di Foo
    • qualche metodo statico probabilmente sovrascritto / getter:
      • this.method()
      • this.property
    • qualche metodo di istanza probabilmente sovrascritto / getter:
      • imansible dal design
    • proprio metodo statico / getter non sovrascritto :
      • Foo.method()
      • Foo.property
    • proprio metodo di istanza non sovrascritto / getter:
      • imansible dal design
  • dal metodo istanza / getter di Foo
    • qualche metodo statico probabilmente sovrascritto / getter:
      • this.constructor.method()
      • this.constructor.property
    • qualche metodo di istanza probabilmente sovrascritto / getter:
      • this.method()
      • this.property
    • proprio metodo statico / getter non sovrascritto :
      • Foo.method()
      • Foo.property
    • proprio metodo di istanza non sovrascritto / getter:
      • non è ansible intenzionalmente se non si utilizza una soluzione alternativa :
        • Foo.prototype.method.call( this )
        • Object.getOwnPropertyDescriptor( Foo.prototype,"property" ).get.call(this);

Tieni presente che l’utilizzo di this non funziona in questo modo quando si utilizzano le funzioni freccia o si invocano metodi / getters associati esplicitamente al valore personalizzato.

sfondo

  • Quando si fa riferimento al metodo o al getter di un’istanza
    • this si riferisce all’istanza corrente.
    • super si riferisce fondamentalmente alla stessa istanza, ma in qualche modo si stanno estendendo metodi e getter scritti nel contesto di una class corrente (usando il prototipo del prototipo di Foo).
    • la definizione della class di istanza utilizzata per crearla è disponibile per this.constructor .
  • Quando nel contesto di un metodo statico o getter non c’è “istanza corrente” per intenzione e così via
    • this è disponibile per fare riferimento direttamente alla definizione della class corrente.
    • super non si riferisce ad alcuna istanza, ma a metodi statici e getter scritti nel contesto di una class corrente si sta estendendo.

Conclusione

Prova questo codice:

 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 ); 

Se stai pensando di fare qualsiasi tipo di ereditarietà, allora ti consiglio this.constructor . Questo semplice esempio dovrebbe illustrare il motivo:

 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() registrerà ConstructorSuper Hello ConstructorSuper! alla console
  • test2.callPrint() registrerà ConstructorSub Hello ConstructorSub! alla console

La class denominata non gestirà l’ereditarietà in modo appropriato a meno che non si ridefinisca esplicitamente ogni funzione che fa riferimento alla class denominata. Ecco un esempio:

 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() registrerà NamedSuper Hello NamedSuper! alla console
  • test4.callPrint() registrerà NamedSuper Hello NamedSub! alla console

Vedi tutto quanto sopra in esecuzione in Babel REPL .

Potete vedere da ciò che test4 pensa ancora che sia nella super class; in questo esempio potrebbe non sembrare un grosso problema, ma se stai provando a fare riferimento a funzioni membro che sono state sostituite oa nuove variabili membro, ti troverai nei guai.