नियमित ES6 वर्ग विधियों से स्थिर विधियों को कॉल करें


176

स्थैतिक तरीकों को कॉल करने का मानक तरीका क्या है? मैं constructorकक्षा के नाम का उपयोग या उपयोग करने के बारे में सोच सकता हूं , मुझे यह पसंद नहीं है क्योंकि यह आवश्यक नहीं लगता है। क्या पूर्व अनुशंसित तरीका है, या कुछ और है?

यहाँ (contrived) उदाहरण है:

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

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

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

8
SomeObject.printस्वाभाविक लगता है। लेकिन this.nअंदर कोई मतलब नहीं है क्योंकि कोई उदाहरण नहीं है, अगर हम स्थैतिक तरीकों के बारे में बात कर रहे हैं।
dfsq

3
@dfsq printNहालांकि स्थिर नहीं है।
सिमोनजैक

आप सही हैं, भ्रमित नाम हैं।
dfsq

1
मैं उत्सुक हूँ कि इस प्रश्न में इतने उभार क्यों नहीं हैं! क्या यह उपयोगिता कार्यों के निर्माण के लिए एक सामान्य अभ्यास नहीं है?
थोरण डे

जवाबों:


211

दोनों तरीके व्यवहार्य हैं, लेकिन जब वे एक स्थैतिक विधि के साथ विरासत में आते हैं, तो वे अलग-अलग चीजें करते हैं। वह व्यवहार चुनें जिसकी आप अपेक्षा करते हैं:

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

वर्ग के माध्यम से स्थैतिक संपत्ति का जिक्र वास्तव में स्थिर होगा और लगातार समान मूल्य देगा। का उपयोग करते हुए this.constructorबजाय गतिशील प्रेषण का उपयोग करें और वर्तमान उदाहरण के वर्ग है, जहां स्थिर संपत्ति का उल्लेख होगा सकता है विरासत में मिला मूल्य है, लेकिन यह भी ओवरराइड हो सकता है।

यह पायथन के व्यवहार से मेल खाता है, जहां आप स्थिर गुणों को संदर्भित करने के लिए या तो क्लास नाम या उदाहरण के माध्यम से चुन सकते हैं self

यदि आप उम्मीद करते हैं कि स्थैतिक गुणों को ओवरराइड नहीं किया जाएगा (और हमेशा वर्तमान वर्ग में से एक को देखें), जैसे जावा में , स्पष्ट संदर्भ का उपयोग करें।


क्या आप कंस्ट्रक्टर प्रॉपर्टी बनाम क्लास मेथड डेफिनिशन की व्याख्या कर सकते हैं?
क्रिस

2
@Chris: हर वर्ग है एक निर्माता समारोह (जैसा कि आपने बिना ES5 से यह पता classवाक्य रचना), वहाँ विधि की परिभाषा में कोई अंतर नहीं है। यह सिर्फ एक बात है कि आप इसे कैसे देखते हैं, विरासत में मिली constructorसंपत्ति या सीधे अपने नाम से।
बर्गी

एक अन्य उदाहरण PHP की लेट स्टेटिक बाइंडिंग है । न केवल यह करता है। अवरोधक वंशानुक्रम का सम्मान करता है, लेकिन यदि आप कक्षा का नाम बदलते हैं तो आपको कोड अपडेट करने से बचने में भी मदद करता है।
रिकोनिथेरुन

जब आप चर नामों को बदलते हैं तो @ricanontherun कोड का उपयोग करते हुए नाम का उपयोग करने के खिलाफ एक तर्क नहीं है। इसके अलावा रीफैक्टरिंग उपकरण इसे वैसे भी स्वचालित रूप से कर सकते हैं।
बर्गी

टाइपस्क्रिप्ट में इसे कैसे लागू किया जाए? यह त्रुटि देता हैProperty 'staticProperty' does not exist on type 'Function'
ayZagen

72

मैं इस मामले में जवाब के लिए खोज इस धागे पर ठोकर खाई। मूल रूप से सभी उत्तर पाए जाते हैं, लेकिन उनमें से आवश्यक सामग्री निकालना अभी भी कठिन है।

पहुँच के प्रकार

एक वर्ग Foo मान लें कि शायद कुछ अन्य वर्ग (तों) से व्युत्पन्न है, शायद उससे अधिक वर्ग।

फिर पहुंच

  • स्थिर विधि / फूटर के गेटटर से
    • कुछ शायद ओवरराइड स्टैटिक मेथड / गेट्टर:
      • this.method()
      • this.property
    • कुछ संभवतः ओवरराइड उदाहरण विधि / गेट्टर:
      • डिजाइन द्वारा असंभव
    • स्वयं गैर-ओवरराइड स्टैटिक विधि / गेट्टर:
      • Foo.method()
      • Foo.property
    • स्वयं गैर-ओवरराइड उदाहरण विधि / गेट्टर:
      • डिजाइन द्वारा असंभव
  • Foo के उदाहरण विधि / गेट्टर से
    • कुछ शायद ओवरराइड स्टैटिक मेथड / गेट्टर:
      • this.constructor.method()
      • this.constructor.property
    • कुछ संभवतः ओवरराइड उदाहरण विधि / गेट्टर:
      • this.method()
      • this.property
    • स्वयं गैर-ओवरराइड स्टैटिक विधि / गेट्टर:
      • Foo.method()
      • Foo.property
    • स्वयं गैर-ओवरराइड उदाहरण विधि / गेट्टर:
      • कुछ वर्कअराउंड का उपयोग करते हुए इरादे से संभव नहीं :
        • Foo.prototype.method.call( this )
        • Object.getOwnPropertyDescriptor( Foo.prototype,"property" ).get.call(this);

ध्यान रखें कि thisइस तरह से काम नहीं कर रहा है जब एरो फ़ंक्शंस का उपयोग कर रहा है या विधियों / प्राप्तकर्ताओं को स्पष्ट रूप से कस्टम मान के लिए बाध्य कर रहा है।

पृष्ठभूमि

  • जब एक उदाहरण की विधि या पाने वाले के संदर्भ में
    • this वर्तमान उदाहरण का जिक्र है।
    • super मूल रूप से एक ही उदाहरण का जिक्र है, लेकिन कुछ वर्ग वर्तमान के संदर्भ में कुछ तरीकों और गेटर्स को संबोधित करते हुए (फू के प्रोटोटाइप के प्रोटोटाइप का उपयोग करके) का विस्तार किया जा रहा है।
    • इसे बनाने पर उपयोग किए जाने वाले इंस्टेंस वर्ग की परिभाषा प्रति उपलब्ध है this.constructor
  • जब एक स्थैतिक विधि या गेट्टर के संदर्भ में कोई इरादा और "द्वारा वर्तमान उदाहरण" नहीं है
    • this सीधे वर्तमान वर्ग की परिभाषा को संदर्भित करने के लिए उपलब्ध है।
    • super कुछ उदाहरणों का भी उल्लेख नहीं कर रहा है, लेकिन स्थिर तरीकों और कुछ वर्ग के संदर्भ में लिखे गए गेट्स का विस्तार हो रहा है।

निष्कर्ष

इस कोड को आज़माएं:

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--- यह एक वास्तविक दया है। मेरी राय में, यह ES6 + की कमी है। हो सकता है कि इसे केवल method- यानी की अनुमति देने के लिए अपडेट किया जाए method.call(this)। से बेहतर है Foo.prototype.method। कोलाहल / आदि। एक NFE (फ़ंक्शन अभिव्यक्ति का नाम) का उपयोग करके कार्यान्वित कर सकता है।
रॉय टिंकर

method.call( this )एक संभावित समाधान methodहै जो तब वांछित आधार "क्लास" के लिए बाध्य नहीं होता है और इस तरह एक गैर-ओवरड्रिन उदाहरण विधि / गेट्टर होने में विफल रहता है । क्लास-इंडिपेंडेंट तरीकों के साथ काम करना हमेशा संभव होता है। फिर भी मुझे नहीं लगता कि मौजूदा डिजाइन इतना बुरा है। आपके बेस क्लास फू से प्राप्त वर्ग की वस्तुओं के संदर्भ में एक इंस्टेंस विधि को ओवरराइड करने के अच्छे कारण हो सकते हैं। उस ओवरराइड विधि के पास इसके superकार्यान्वयन को लागू करने के लिए अच्छे कारण हो सकते हैं या नहीं। या तो मामला पात्र है और उसका पालन होना चाहिए। अन्यथा यह खराब ओओपी डिजाइन में समाप्त हो जाएगा।
थॉमस अर्बन

ओओपी चीनी के बावजूद, ईएस विधियां अभी भी कार्य हैं , और लोग उन्हें इस तरह से उपयोग और संदर्भ देना चाहेंगे। ES क्लास सिंटैक्स के साथ मेरी समस्या यह है कि यह वर्तमान में निष्पादित पद्धति का कोई प्रत्यक्ष संदर्भ प्रदान नहीं करता है - कुछ ऐसा जो arguments.calleeएनएफई के माध्यम से आसान हुआ करता था ।
रॉय टिंकर

वैसे भी बुरा अभ्यास या कम से कम खराब सॉफ़्टवेयर डिज़ाइन जैसा लगता है। मैं दोनों बिंदुओं को एक-दूसरे के विपरीत मानता हूं, क्योंकि मुझे ओओपी प्रतिमान के संदर्भ में योग्य कारण दिखाई नहीं देता है जिसमें वर्तमान में संदर्भ द्वारा विधि का उपयोग करना शामिल है (जो कि इसके संदर्भ के रूप में उपलब्ध नहीं है this)। यह उच्च-स्तरीय सी # के साथ नंगे सी के सूचक अंकगणित के लाभों को मिश्रण करने की कोशिश करने जैसा लगता है। बस जिज्ञासा से बाहर: आप arguments.calleeसाफ-सुथरे ओओपी कोड के लिए क्या उपयोग करेंगे ?
थॉमस अर्बन

मैं Dojo के क्लास सिस्टम के साथ निर्मित एक बड़े प्रोजेक्ट में काम कर रहा हूं, जो वर्तमान पद्धति के सुपरक्लास (es) कार्यान्वयन (एस) को कॉल करने की अनुमति देता है this.inherited(currentFn, arguments);- जहां currentFnवर्तमान में निष्पादित फ़ंक्शन का संदर्भ है। वर्तमान में निष्पादित फ़ंक्शन को सीधे संदर्भित करने में सक्षम नहीं होने के कारण यह टाइपस्क्रिप्ट में थोड़ा बालों वाला बना रहा है, जो ईएस 6 से अपनी श्रेणी के सिंटैक्स लेता है।
रॉय टिंकर

20

यदि आप किसी भी प्रकार की विरासत करने की योजना बना रहे हैं, तो मैं सिफारिश करूंगा this.constructor। यह सरल उदाहरण स्पष्ट करना चाहिए कि क्यों:

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!कंसोल पर लॉग इन करेंगे
  • test2.callPrint()ConstructorSub Hello ConstructorSub!कंसोल पर लॉग इन करेंगे

नामित वर्ग अच्छी तरह से विरासत के साथ सौदा नहीं करेगा जब तक कि आप स्पष्ट रूप से हर फ़ंक्शन को फिर से परिभाषित नहीं करते हैं जो नामित वर्ग का संदर्भ बनाता है। यहाँ एक उदाहरण है:

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!कंसोल पर लॉग इन करेंगे
  • test4.callPrint()NamedSuper Hello NamedSub!कंसोल पर लॉग इन करेंगे

बाबेल आरईपीएल में उपर्युक्त सभी देखें

आप इस से देख सकते हैं कि test4अभी भी लगता है कि यह सुपर क्लास में है; इस उदाहरण में यह एक बहुत बड़ी बात नहीं हो सकती है, लेकिन यदि आप ऐसे सदस्य कार्यों को संदर्भित करने का प्रयास कर रहे हैं जो अतिरंजित या नए सदस्य चर हैं, तो आप खुद को परेशानी में पाएंगे।


3
लेकिन स्थैतिक कार्य कोई अधिरोहित सदस्य विधियाँ नहीं हैं? आमतौर पर आप कोशिश कर रहे हैं कि किसी भी ओवरराइड सामान को स्टैटिकली रेफर न करें।
बरगी

1
@Bergi मुझे यकीन नहीं है कि मैं समझता हूं कि आप क्या इंगित कर रहे हैं, लेकिन एक विशिष्ट मामला जो मैं सामने आया हूं वह एमवीसी मॉडल हाइड्रेट्स पैटर्न के साथ होगा। एक मॉडल का विस्तार करने वाली उप कक्षाएं एक स्थिर हाइड्रेट फ़ंक्शन को लागू करना चाह सकती हैं। हालाँकि, जब इनमें हार्ड-कोडिंग की जाती है, बेस मॉडल इंस्टेंस केवल कभी लौटाए जाते हैं। यह एक बहुत विशिष्ट उदाहरण है, लेकिन कई पैटर्न जो पंजीकृत उदाहरणों के एक स्थिर संग्रह पर भरोसा करते हैं, वे इससे प्रभावित होंगे। एक बड़ा डिस्क्लेमर यह है कि हम यहां प्रादेशिक विरासत की बजाए यहां शास्त्रीय विरासत का अनुकरण करने का प्रयास कर रहे हैं ... और यह लोकप्रिय नहीं है: P
एंड्रयू ओड्री

हाँ, जैसा कि मैंने अब अपने स्वयं के उत्तर में यह निष्कर्ष निकाला है कि यह "शास्त्रीय" विरासत में लगातार हल नहीं होता है - कभी-कभी आप ओवरराइड्स चाहते हैं, कभी-कभी नहीं। मेरी टिप्पणी का पहला हिस्सा स्थिर वर्ग के कार्यों पर इंगित किया गया था, जिसे मैं "सदस्य" नहीं मानता था। इसे बेहतर तरीके से नजरअंदाज करें :-)
बर्गी
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.