ES6 में सुपर का उपयोग किए बिना एक वर्ग का विस्तार कैसे करें?


98

क्या superमूल वर्ग को आह्वान करने के लिए विधि को बुलाए बिना ES6 में कक्षा का विस्तार करना संभव है ?

संपादित करें: प्रश्न भ्रामक हो सकता है। क्या यह मानक है कि हमें फोन करना है super()या मुझे कुछ याद आ रहा है?

उदाहरण के लिए:

class Character {
   constructor(){
      console.log('invoke character');
   }
}

class Hero extends Character{
  constructor(){
      super(); // exception thrown here when not called
      console.log('invoke hero');
  }
}

var hero = new Hero();

जब मैं super()व्युत्पन्न वर्ग पर कॉल नहीं कर रहा हूँ तो मुझे एक गुंजाइश समस्या हो रही है ->this is not defined

मैं इसे v2.3.0 में iojs --harmony के साथ चला रहा हूं


गुंजाइश समस्या का क्या मतलब है? क्या आपको अपवाद मिल रहा है (और कहां)?
अमित

सुपर () बुलाए बिना इसे लागू करने पर मुझे अपने व्युत्पन्न वर्ग में समाप्ति मिल रही है। मैंने अपने प्रश्न को और अधिक स्पष्ट बनाने के लिए संपादित किया :)
xhallix

आप इसे किस माहौल में चला रहे हैं?
अमित

6
आपके पास कोई विकल्प नहीं है यदि आप किसी अन्य वर्ग का विस्तार करते हैं तो निर्माता को पहले सुपर () कॉल करना होगा।
जोनाथन डे एम।

@JonathandeM। धन्यवाद, तो यह उस तरह से है जैसा कि मुझे लगता है कि भविष्य में होना चाहिए था?
xhallix

जवाबों:


147

ES2015 (ES6) कक्षाओं के लिए नियम मूल रूप से नीचे आते हैं:

  1. चाइल्ड क्लास कंस्ट्रक्टर में, thisतब तक इस्तेमाल नहीं किया जा सकता है जब तक superकि उसे कॉल न किया जाए ।
  2. ES6 क्लास कंस्ट्रक्टर्स MUST कॉल superकरते हैं अगर वे उपवर्ग हैं, या उन्हें स्पष्ट रूप से उस ऑब्जेक्ट की जगह लेने के लिए कुछ ऑब्जेक्ट वापस करना होगा जो कि आरंभीकृत नहीं था।

यह ES2015 कल्पना के दो महत्वपूर्ण खंडों में आता है।

धारा 8.1.1.3.4this फ़ंक्शन में क्या है यह तय करने के लिए तर्क को परिभाषित करता है। कक्षाओं के लिए महत्वपूर्ण हिस्सा यह है कि यह thisएक "uninitialized"राज्य में होना संभव है , और जब इस राज्य में उपयोग करने का प्रयास किया जाता हैthis एक अपवाद फेंक देगा।

धारा 9.2.2 , [[Construct]]जो कार्यों के व्यवहार को परिभाषित करता है newया कहा जाता है super। बेस क्लास कंस्ट्रक्टर को कॉल करते समय, thisचरण # 8 पर आरंभ किया जाता है [[Construct]], लेकिन अन्य सभी मामलों के लिए, thisयह अनइंस्टॉलिज्ड है। निर्माण के अंत में, GetThisBindingकहा जाता है, इसलिए यदि superअभी तक नहीं बुलाया गया है (इस प्रकार प्रारंभिक this), या एक स्पष्ट प्रतिस्थापन ऑब्जेक्ट वापस नहीं किया गया था, तो कंस्ट्रक्टर कॉल की अंतिम पंक्ति एक अपवाद फेंक देगी।


1
क्या आप बिना बुलाए एक वर्ग से विरासत का रास्ता सुझा सकते हैं super()?
बर्गी

4
क्या आपको लगता है कि ES6 में यह संभव super()है कि कंस्ट्रक्टर में कॉल किए बिना एक वर्ग से विरासत में मिले ?
बर्गी

8
संपादन के लिए धन्यवाद - यह अभी वहीं है; आप return Object.create(new.target.prototype, …)सुपर कंस्ट्रक्टर को कॉल करने से बचने के लिए कर सकते हैं ।
बरगी


1
यदि निर्माणकर्ता को छोड़ दिया गया है, तो आपको जोड़ना चाहिए: MDN के अनुसार एक डिफ़ॉल्ट निर्माणकर्ता का उपयोग किया जाएगा ।
kleinfreund 14

11

बताते चले कि super MUST के अंदर पहली लाइन होनी चाहिएconstructor । वह बस गलत है। @loganfsmyth उत्तर में आवश्यकताओं के आवश्यक संदर्भ हैं, लेकिन यह निम्नलिखित है:

इनहेरिटिंग ( extends) कंस्ट्रक्टर कोsuper उपयोग करने thisसे पहले और लौटने से पहले भी कॉल करना होगाthis उसका उपयोग न किया गया हो

नीचे टुकड़ा देखें (क्रोम में काम करता है ...) यह देखने के लिए कि thisकॉल करने से पहले बयान (बिना उपयोग किए ) का अर्थ क्यों हो सकता है super

'use strict';
var id = 1;
function idgen() {
  return 'ID:' + id++;
}

class Base {
  constructor(id) {
    this.id = id;
  }

  toString() { return JSON.stringify(this); }
}

class Derived1 extends Base {
  constructor() {
    var anID = idgen() + ':Derived1';
    super(anID);
    this.derivedProp = this.baseProp * 2;
  }
}

alert(new Derived1());


2
"See fragment below (works in Chrome...)"क्रोम डेवलपर कंसोल खोलें और "रन कोड स्निपेट" पर क्लिक करें Uncaught ReferenceError: this is not defined:। यकीन है, आप पहले विधायक में विधियों का उपयोग कर सकते हैं, super()लेकिन आप कक्षा से पहले के तरीकों का उपयोग नहीं कर सकते हैं!
मार्स

thisइससे पहले कि आप उपयोग नहीं कर सकते super()(आपका कोड यह साबित करता है) का विनिर्देशन के साथ तुरंत कोई लेना-देना नहीं है, लेकिन जावास्क्रिप्ट के कार्यान्वयन के साथ। तो, आपको 'इस' से पहले 'सुपर' को कॉल करना होगा।
मार्सेल

@marcel - मुझे लगता है कि हमें बहुत भ्रम था। मैं केवल (सभी के साथ) यह कह रहा था कि उपयोग करने से पहले आँकड़े होना कानूनी है super, और आप कह रहे थे कि thisकॉल करने से पहले इसका उपयोग करना अवैध है super। हम दोनों सही हैं, बस एक दूसरे को समझ नहीं पाए :-) (और यह अपवाद जानबूझकर था, यह दिखाने के लिए कि कानूनी नहीं है - मैंने संपत्ति का नाम भी 'विलफेल' रखा है)
अमित

9

नया es6 वर्ग वाक्यविन्यास केवल प्रोटोटाइप के साथ "पुरानी" es5 "कक्षाओं" के लिए एक अन्य संकेतन है। इसलिए आप इसके प्रोटोटाइप (बेस क्लास) को सेट किए बिना किसी विशिष्ट वर्ग को तुरंत नहीं भेज सकते।

यह आपके सैंडविच पर पनीर बनाना पसंद करता है। इसके अलावा आप पहले पनीर नहीं डाल सकते हैं सैंडविच बनाने से , इसलिए ...

... thisसुपर क्लास को कॉल करने से पहले कीवर्ड का उपयोग करने super()की अनुमति नहीं है, भी।

// valid: Add cheese after making the sandwich
class CheeseSandwich extend Sandwich {
    constructor() {
        super();
        this.supplement = "Cheese";
    }
}

// invalid: Add cheese before making sandwich
class CheeseSandwich extend Sandwich {
    constructor() {
        this.supplement = "Cheese";
        super();
    }
}

// invalid: Add cheese without making sandwich
class CheeseSandwich extend Sandwich {
    constructor() {
        this.supplement = "Cheese";
    }
}

यदि आप बेस क्लास के लिए कंस्ट्रक्टर निर्दिष्ट नहीं करते हैं, तो निम्न परिभाषा का उपयोग किया जाता है:

constructor() {}

व्युत्पन्न वर्गों के लिए, निम्नलिखित डिफ़ॉल्ट कंस्ट्रक्टर का उपयोग किया जाता है:

constructor(...args) {
    super(...args);
}

संपादित करें: इस पर मिला developer.mozilla.org:

When used in a constructor, the super keyword appears alone and must be used before the this keyword can be used.

स्रोत


इसलिए यदि मैं आपको सही तरीके से समझूं, तो मैं अपनी कक्षा के हीरो पर क्लास कैरेक्टर से किसी भी तरीके का उपयोग नहीं कर पाऊंगा, जब सुपर () को लागू नहीं कर रहा हूं? लेकिन यह पूरी तरह से सही नहीं लगता है, क्योंकि मैं बेस क्लास से विधियों को कॉल कर सकता हूं। इसलिए मुझे लगता है कि मुझे बस निर्माता को फोन करते समय सुपर की आवश्यकता है
xhallix

1. ओपी में, thisबिल्कुल इस्तेमाल नहीं किया जाता है। 2. जेएस एक सैंडविच नहीं है, और ईएस 5 में आप हमेशा उपयोग कर सकते हैं this, यहां तक ​​कि आपको किसी भी अन्य फ़ंक्शन को कॉल करने से पहले भी (जैसे कि आप पूरक संपत्ति को परिभाषित नहीं कर सकते हैं)
अमित

@amit 1. और अब मैं thisभी उपयोग नहीं कर सकते हैं ?! 2. मेरा जेएस वर्ग एक सैंडविच का प्रतिनिधित्व करता है, और ईएस 6 में आप हमेशा उपयोग नहीं कर सकते this। मैं बस es6 वर्गों (एक रूपक के साथ) को समझाने की कोशिश कर रहा हूं, और किसी को भी इस तरह के विनाशकारी / अनावश्यक टिप्पणियों की आवश्यकता नहीं है।
मार्स

@marcel cynicism के लिए मेरी माफी, लेकिन: 1. ओपी में मौजूद नहीं थी एक नई समस्या के बारे में था। 2 यह ध्यान देने के लिए है कि आपका दावा गलत है (जो अभी भी मामला है)
अमित

@marcel - मेरा जवाब देखें
अमित

4

बस इस समाधान को पोस्ट करने के लिए पंजीकृत किया गया है क्योंकि यहां उत्तर मुझे कम से कम संतुष्ट नहीं करते हैं क्योंकि वास्तव में इसके आसपास एक सरल तरीका है। केवल सुपर कंस्ट्रक्टर का उपयोग करते हुए अपने तर्क को उप-विधि में अधिलेखित करने के लिए अपने वर्ग-निर्माण पैटर्न को समायोजित करें और इसके लिए कंस्ट्रक्टर तर्क को अग्रेषित करें।

जैसा कि आप अपने उपवर्गों में प्रति से एक निर्माता नहीं बनाते हैं, लेकिन केवल एक विधि का संदर्भ देते हैं जो संबंधित उपवर्ग में ओवरराइड होता है।

इसका मतलब है कि आप अपने आप को अपने ऊपर लागू किए गए निर्माण कार्य से मुक्त कर सकते हैं और एक नियमित पद्धति से बच सकते हैं - जिसे ओवरराइड किया जा सकता है और सुपर को लागू नहीं करता है () अपने आप को यह पसंद करने पर कि आप कहाँ और कैसे सुपर कॉल करना चाहते हैं (पूरी तरह से वैकल्पिक) उदा:

super.ObjectConstructor(...)

class Observable {
  constructor() {
    return this.ObjectConstructor(arguments);
  }

  ObjectConstructor(defaultValue, options) {
    this.obj = { type: "Observable" };
    console.log("Observable ObjectConstructor called with arguments: ", arguments);
    console.log("obj is:", this.obj);
    return this.obj;
  }
}

class ArrayObservable extends Observable {
  ObjectConstructor(defaultValue, options, someMoreOptions) {
    this.obj = { type: "ArrayObservable" };
    console.log("ArrayObservable ObjectConstructor called with arguments: ", arguments);
    console.log("obj is:", this.obj);
    return this.obj;
  }
}

class DomainObservable extends ArrayObservable {
  ObjectConstructor(defaultValue, domainName, options, dependent1, dependent2) {
    this.obj = super.ObjectConstructor(defaultValue, options);
    console.log("DomainObservable ObjectConstructor called with arguments: ", arguments);
    console.log("obj is:", this.obj);
    return this.obj;
  }
}

var myBasicObservable = new Observable("Basic Value", "Basic Options");
var myArrayObservable = new ArrayObservable("Array Value", "Array Options", "Some More Array Options");
var myDomainObservable = new DomainObservable("Domain Value", "Domain Name", "Domain Options", "Dependency A", "Depenency B");

चियर्स!


2
मुझे इस पर एक "मैं पाँच की तरह समझाता हूँ" की आवश्यकता है .. मुझे ऐसा लगता है कि यह एक बहुत ही गहरा जवाब है लेकिन जटिल है और इसके बाद नजरअंदाज किया गया है
swyx

@swyx: जादू कंस्ट्रक्टर के अंदर होता है, जहां 'यह' एक अलग प्रकार की वस्तु को संदर्भित करता है जो इस बात पर निर्भर करता है कि आप किस प्रकार की वस्तु बना रहे हैं। उदाहरण के लिए, यदि आप एक नया DomainObservable का निर्माण कर रहे हैं, तो यह .OjectConstructor एक अलग विधि अर्थात DomainObserveable.ObjectConstructor को संदर्भित करता है; जब आप एक नया ArrayObservable का निर्माण कर रहे हैं, तो यह.ObjectConstructor ArrayObservable.ObjectConstructor को संदर्भित करता है।
कोबर्बॉय

मेरा जवाब देखें, मैंने एक बहुत सरल उदाहरण पोस्ट किया
माइकल लुईस

मैं पूरी तरह से @swyx से सहमत हूं; यह जवाब बहुत ज्यादा हो रहा है ... मैंने केवल इसे स्किम किया है और मैं पहले ही थक गया हूं। मुझे लग रहा है "जैसे समझाता हूँ कि मैं पाँच हूँ और वास्तव में पेशाब करना है ..."
spb

4

यदि आप अपने उपवर्ग में पूरी तरह से कंस्ट्रक्टर को छोड़ देते हैं, तो आप अपने उपवर्ग में सुपर () को छोड़ सकते हैं। एक 'छिपा हुआ' डिफॉल्ट कंस्ट्रक्टर आपके उपवर्ग में अपने आप शामिल हो जाएगा। हालाँकि, यदि आप कंस्ट्रक्टर को अपने उपवर्ग में शामिल करते हैं, तो सुपर () को उस कंस्ट्रक्टर में बुलाया जाना चाहिए।

class A{
   constructor(){
      this.name = 'hello';   
   }
}
class B extends A{
   constructor(){
      // console.log(this.name); // ReferenceError
      super();
      console.log(this.name);
   }
}
class C extends B{}  // see? no super(). no constructor()

var x = new B; // hello
var y = new C; // hello

अधिक जानकारी के लिए इसे पढ़ें ।


2

जस्ट्यूरेज का जवाब सबसे आसान तरीका है, लेकिन उसका उदाहरण थोड़ा फूला हुआ है। यहाँ जेनेरिक संस्करण है:

class Base {
    constructor(){
        return this._constructor(...arguments);
    }

    _constructor(){
        // just use this as the constructor, no super() restrictions
    }
}

class Ext extends Base {
    _constructor(){ // _constructor is automatically called, like the real constructor
        this.is = "easy"; // no need to call super();
    }
}

असली का विस्तार न करें constructor(), बस _constructor()तात्कालिक तर्क के लिए नकली का उपयोग करें ।

ध्यान दें, यह समाधान डिबगिंग को कष्टप्रद बनाता है क्योंकि आपको प्रत्येक तात्कालिकता के लिए एक अतिरिक्त विधि में कदम रखना होगा।


हां, यह अब तक का सबसे आसान तरीका है और सबसे स्पष्ट उत्तर है - जो प्रश्न मैं पूछता हूं वह है ... "क्यों, भगवान, क्यों?!" ... एक बहुत ही मान्य कारण है कि सुपर () स्वचालित नहीं है। .. आप अपने बेस क्लास के लिए विशिष्ट पैरामीटर पास करना चाह सकते हैं, ताकि आप बेस क्लास को तुरंत शुरू करने से पहले कुछ प्रोसेसिंग / लॉजिक / सोच कर सकें।
LFLFM

1

प्रयत्न:

class Character {
   constructor(){
     if(Object.getPrototypeOf(this) === Character.prototype){
       console.log('invoke character');
     }
   }
}


class Hero extends Character{
  constructor(){
      super(); // throws exception when not called
      console.log('invoke hero');
  }
}
var hero = new Hero();

console.log('now let\'s invoke Character');
var char = new Character();

Demo


इस उदाहरण में आप सुपर () का उपयोग करते हैं और यदि आप इसे छोड़ देते हैं, तो आपके पास एक अपवाद है। इसलिए मुझे लगता है कि इस मामले में इस सुपर () कॉल को छोड़ना संभव नहीं है
xhallix

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

भ्रामक मधुमक्खी के लिए खेद है: नहीं, मैं सिर्फ यह जानना चाहता था कि क्या वास्तव में सुपर का उपयोग करना आवश्यक है () क्योंकि मैं वाक्य रचना के बारे में आश्चर्य करता हूं क्योंकि अन्य भाषाओं में हमें व्युत्पन्न वर्ग के लिए निर्माणकर्ता को बुलाते समय सुपर विधि को लागू करने की आवश्यकता नहीं है
xhallix

1
Developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/… , के अनुसार A constructor *can* use the super keyword to call the constructor of a parent class., इसलिए मैं कहूंगा कि ES6 रिलीज़ की प्रतीक्षा करें
जोनाथन डे एम।

3
"इसलिए मैं ईएस 6
zerkms

1

यदि आप OOP अवधारणाओं का विकास करना चाहते हैं तो मैं OODK-JS का उपयोग करने की सलाह दूंगा

OODK(function($, _){

var Character  = $.class(function ($, µ, _){

   $.public(function __initialize(){
      $.log('invoke character');
   });
});

var Hero = $.extends(Character).class(function ($, µ, _){

  $.public(function __initialize(){
      $.super.__initialize();
      $.log('invoke hero');
  });
});

var hero = $.new(Hero);
});

1

सरल समाधान: मुझे लगता है कि इसकी स्पष्ट व्याख्या की कोई आवश्यकता नहीं है।

class ParentClass() {
    constructor(skipConstructor = false) { // default value is false
        if(skipConstructor) return;
        // code here only gets executed when 'super()' is called with false
    }
}
class SubClass extends ParentClass {
    constructor() {
        super(true) // true for skipping ParentClass's constructor.
        // code
    }
}

मुझे यकीन नहीं है कि उपरोक्त सभी बॉयलरप्लेट कोड, और मुझे बिल्कुल यकीन नहीं है कि क्या इस दृष्टिकोण के दुष्प्रभाव हैं। यह मेरे लिए बिना किसी समस्या के काम करता है।
MyUserInStackOverflow

1
एक मुद्दा: यदि आप अपने सबक्लास को फिर से आगे बढ़ाना चाहते हैं, तो आपको स्किपक्लास्ट्रक्टर फीचर को प्रत्येक सबक्लास के कंस्ट्रक्टर में बनाना होगा
माइकल लुईस

0

@Bergi ने उल्लेख किया है new.target.prototype, लेकिन मैं एक ठोस उदाहरण की तलाश कर रहा था जो यह साबित कर रहा हो कि आप thisजिस क्लाइंट कोड के साथ निर्माण कर रहे हैं new, उसका संदर्भ ( नीचे देख सकते हैं, नीचे देखें) बिना कॉल किए super()

बात सस्ती है, मुझे कोड दिखाओ ... तो यहाँ एक उदाहरण है:

class A { // Parent
    constructor() {
        this.a = 123;
    }

    parentMethod() {
        console.log("parentMethod()");
    }
}

class B extends A { // Child
    constructor() {
        var obj = Object.create(new.target.prototype)
        // You can interact with obj, which is effectively your `this` here, before returning
        // it to the caller.
        return obj;
    }

    childMethod(obj) {
        console.log('childMethod()');
        console.log('this === obj ?', this === obj)
        console.log('obj instanceof A ?', obj instanceof A);
        console.log('obj instanceof B ?',  obj instanceof B);
    }
}

b = new B()
b.parentMethod()
b.childMethod(b)

जो आउटपुट देगा:

parentMethod()
childMethod()
this === obj ? true
obj instanceof A ? true
obj instanceof B ? true

तो आप देख सकते हैं कि हम प्रभावी रूप से एक प्रकार की वस्तु B(चाइल्ड क्लास) बना रहे हैं, जो कि एक प्रकार की वस्तु भी है A(इसका मूल वर्ग) और childMethod()बच्चे के अंदर Bहम thisउस वस्तु की ओर इशारा करते हैं objजिसे हमने बी के constructorसाथ बनाया है Object.create(new.target.prototype)

और यह सब परवाह किए बिना super

यह इस तथ्य का लाभ उठाता है कि जेएस constructorमें एक पूरी तरह से अलग वस्तु वापस आ सकती है जब ग्राहक कोड के साथ एक नया उदाहरण बनाता है new

आशा है कि यह किसी की मदद करता है।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.