Statik yöntemleri normal ES6 sınıfı yöntemlerinden çağırın


176

Statik yöntemleri çağırmanın standart yolu nedir? constructorSınıfın adını kullanmayı veya kullanmayı düşünebilirim, gerekli hissetmediği için ikincisini sevmiyorum. İlki önerilen yol mu, yoksa başka bir şey var mı?

İşte (tutarlı) bir örnek:

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

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

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

8
SomeObject.printdoğal hissediyor. Fakat this.niçte hiçbir anlam ifade etmiyor çünkü statik yöntemler hakkında konuşuyorsak örnek yok.
dfsq

3
@ dfsq printNdurağan değil.
simonzack

Haklısın, kafası karışmış isimler.
dfsq

1
Bu sorunun neden bu kadar çok oyu olmadığını merak ediyorum! Bu, yardımcı işlevler oluşturmak için yaygın bir uygulama değil mi?
Thoran

Yanıtlar:


211

Her iki yol da uygulanabilir, ancak kalıtım söz konusu olduğunda geçersiz kılınmış bir statik yöntemle farklı şeyler yaparlar. Davranışını beklediğinizi seçin:

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

Sınıf aracılığıyla statik özelliğe atıfta bulunmak aslında statik olacaktır ve sürekli olarak aynı değeri verecektir. Kullanımı this.constructordinamik sevkini kullanmak ve statik özelliği, geçerli örneğinde sınıfına, sevk edecektir yerine belki miras değere sahip değil, aynı zamanda geçersiz kılınan olabilir.

Bu, sınıf adı veya örnek üzerinden statik özelliklere başvurmayı seçebileceğiniz Python'un davranışıyla eşleşir self.

Java'da olduğu gibi statik özelliklerin geçersiz kılınmamasını (ve her zaman geçerli sınıftan birine başvurmasını) bekliyorsanız, açık başvuruyu kullanın.


yapıcı özelliğini vs sınıf yöntemi tanımını açıklayabilir misiniz?
Chris

2
@Chris: Her sınıf olan bir yapıcı işlevi (eğer olmadan ES5 dan bildiğimiz gibi classsözdizimi), yöntemin tanımında hiçbir fark yoktur. Bu , miras alınan constructormülk yoluyla veya doğrudan adıyla nasıl göründüğünüzle ilgilidir .
Bergi

Başka bir örnek PHP'nin Geç Statik Bağlamalarıdır . Bunu yapmakla kalmaz, aynı zamanda sınıf adını değiştirirseniz kodu güncellemekten kaçınmanıza da yardımcı olur.
ricanontherun

@ricanontherun Değişken adlarını değiştirdiğinizde kodu güncellemeniz, ad kullanmaya karşı bir argüman değildir. Ayrıca yeniden düzenleme araçları yine de otomatik olarak yapabilir.
Bergi

Daktiloda nasıl uygulanır? Hata veriyorProperty 'staticProperty' does not exist on type 'Function'
ayZagen

72

Benzer konuya cevap arayan bu iş parçacığı üzerinde tökezledi. Temel olarak tüm cevaplar bulunur, ancak onlardan gerekli olanları çıkarmak hala zordur.

Erişim Türleri

Muhtemelen başka sınıflardan türetilen bir Foo sınıfından, muhtemelen daha fazla sınıftan türetilmiş olduğunu varsayalım.

Sonra erişim

  • statik yöntemden / Foo'dan
    • muhtemelen geçersiz kılınmış statik yöntem / alıcı:
      • this.method()
      • this.property
    • muhtemelen geçersiz kılınan örnek yöntemi / alıcısı:
      • tasarım imkansız
    • kendi üzerine geçersiz kılınmamış statik yöntem / alıcı:
      • Foo.method()
      • Foo.property
    • kendi üzerine geçersiz kılınan örnek yöntemi / alıcısı:
      • tasarım imkansız
  • Foo örnek yönteminden / alıcısından
    • muhtemelen geçersiz kılınmış statik yöntem / alıcı:
      • this.constructor.method()
      • this.constructor.property
    • muhtemelen geçersiz kılınan örnek yöntemi / alıcısı:
      • this.method()
      • this.property
    • kendi üzerine geçersiz kılınmamış statik yöntem / alıcı:
      • Foo.method()
      • Foo.property
    • kendi üzerine geçersiz kılınan örnek yöntemi / alıcısı:
      • bazı geçici çözümler kullanılmadıkça niyetle mümkün değildir :
        • Foo.prototype.method.call( this )
        • Object.getOwnPropertyDescriptor( Foo.prototype,"property" ).get.call(this);

thisOk işlevlerini kullanırken veya yöntemleri / alıcıları açıkça özel değere bağlıyken kullanmanın bu şekilde çalışmadığını unutmayın .

Arka fon

  • Bir örneğin yöntemi veya alıcısı bağlamında
    • this mevcut örneğe atıfta bulunuyor.
    • super temelde aynı örneğe atıfta bulunmakta, ancak bir şekilde sınıf akımları bağlamında yazılan yöntemlere ve alıcılara değinmektedir (Foo'nun prototipinin prototipini kullanarak).
    • örneği oluştururken kullanılan sınıfın tanımına göre kullanılabilir this.constructor.
  • Statik bir yöntem veya alıcı bağlamında, niyeti gereği "şu anki örnek" yoktur.
    • this geçerli sınıfın tanımını doğrudan ifade etmek için kullanılabilir.
    • super bazı örneklere atıfta bulunmamakla birlikte, bazı sınıf akımları bağlamında yazılan statik yöntemlere ve alıcılara da uzanmaktadır.

Sonuç

Bu kodu deneyin:

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


1
Own non-overridden instance method/getter / not possible by intention unless using some workaround--- Bu çok yazık. Bence bu ES6 + 'nın bir eksikliği. Belki sadece method- yani method.call(this). Daha iyi Foo.prototype.method. Babil / vb. bir NFE (adlandırılmış işlev ifadesi) kullanarak gerçekleştirebilir.
Roy Tinker

method.call( this )methodistenen temel "sınıf" a bağlı olmayanlar dışında olası bir çözümdür ve bu nedenle geçersiz kılınmış olmayan bir örnek yöntemi / alıcısı olamaz . Sınıftan bağımsız yöntemlerle bu şekilde çalışmak her zaman mümkündür. Yine de mevcut tasarımın o kadar kötü olduğunu düşünmüyorum. Temel sınıf Foo'dan türetilen sınıf nesneleri bağlamında, bir örnek yöntemini geçersiz kılmanın iyi nedenleri olabilir. Bu geçersiz kılma yönteminin superuygulanmasını başlatmak için iyi nedenleri olabilir . Her iki dava da uygundur ve bunlara uyulmalıdır. Aksi takdirde kötü OOP tasarımıyla sonuçlanacaktır.
Thomas Urban

OOP şeker rağmen, yöntemler hala ES fonksiyonları ve insanlar kullanabilir ve bu şekilde onları başvurmak isteyeceksiniz. ES sınıfı sözdizimiyle ilgili sorunum, şu anda yürütülen yönteme doğrudan bir referans sağlaması - eskiden kolay olan bir şey arguments.calleeveya bir NFE.
Roy Tinker

Zaten kötü uygulama ya da en azından kötü yazılım tasarımı gibi geliyor. OOP paradigması bağlamında, şu anda çağrılan yönteme referansla erişmeyi içeren uygun bir neden göremediğim için, her iki bakış açısını da birbirine zıt olarak düşünürdüm this. Çıplak C'nin işaretçi aritmetiğinin avantajlarını daha yüksek düzey C # ile karıştırmaya çalışmak gibi geliyor. Sadece meraktan: arguments.calleetemiz tasarlanmış OOP kodunda ne için kullanırdınız ?
Thomas Urban

Şu anda yürütülmekte olan işleve bir başvuru this.inherited(currentFn, arguments);olduğu - geçerli yöntemin üst sınıf (lar) uygulama (lar) çağrılmasını sağlayan Dojo sınıf sistemi ile inşa edilmiş büyük bir projede çalışıyorum currentFn. Şu anda yürütülen işleve doğrudan başvuramamak, sınıf sözdizimini ES6'dan alan TypeScript'te biraz kıllı hale getiriyor.
Roy Tinker

20

Herhangi bir miras yapmayı planlıyorsanız, tavsiye ederim this.constructor. Bu basit örnek, nedenini göstermelidir:

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()ConstructorSuper Hello ConstructorSuper!konsola giriş yapacak
  • test2.callPrint()ConstructorSub Hello ConstructorSub!konsola giriş yapacak

Adlandırılmış Sınıfa başvuru yapan her işlevi açıkça yeniden tanımlamazsanız, adlandırılmış sınıf kalıtımla güzelce ilgilenmez. İşte bir örnek:

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()NamedSuper Hello NamedSuper!konsola giriş yapacak
  • test4.callPrint()NamedSuper Hello NamedSub!konsola giriş yapacak

Yukarıdakilerin tümünü Babel REPL'de bulabilirsiniz .

Bundan test4hala süper sınıfta olduğunu düşünen görebilirsiniz ; bu örnekte büyük bir anlaşma gibi görünmeyebilir, ancak geçersiz kılınan üye işlevlerine veya yeni üye değişkenlere başvurmaya çalışıyorsanız, kendinizi belada bulacaksınız.


3
Ancak statik işlev geçersiz kılınan üye yöntemleri değildir? Genellikle çalışıyoruz değil statik olarak geçersiz kılınmış şeyler başvurmak için.
Bergi

1
@Bergi Neyi işaret ettiğinizi anladığımdan emin değilim, ancak karşılaştığım özel bir durum MVC modeli hidrasyon desenleri ile olacaktır. Bir modeli genişleten alt sınıflar, statik bir hidrat fonksiyonu uygulamak isteyebilir. Bununla birlikte, bunlar sabit olarak kodlandığında, temel model örnekleri yalnızca döndürülür. Bu oldukça spesifik bir örnektir, ancak kayıtlı örneklerin statik bir koleksiyonuna sahip olan birçok örüntü bundan etkilenecektir. Büyük bir sorumluluk reddi, prototipal kalıtım yerine burada klasik kalıtımı simüle etmeye çalıştığımız ... Ve bu popüler değil: P
Andrew Odri

Evet, kendi cevabımda şu sonuca varmış olduğum gibi, bu, “klasik” kalıtımda bile tutarlı bir şekilde çözülmüyor - bazen geçersiz kılmaları isteyebilirsiniz, bazen de istemeyebilirsiniz. Yorumumun ilk kısmı, "üye" olarak değerlendirmediğim statik sınıf işlevlerine işaret etti. Daha iyi görmezden
gelin
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.