जावास्क्रिप्ट वंशानुक्रम: सुपर-कंस्ट्रक्टर को कॉल करें या प्रोटोटाइप श्रृंखला का उपयोग करें?


82

हाल ही में मैंने एमडीसी में जावास्क्रिप्ट कॉल उपयोग के बारे में पढ़ा

https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Function/call

नीचे दिखाए गए उदाहरण का एक लिंक, मुझे अभी भी समझ नहीं आया है।

वे इस तरह से विरासत का उपयोग क्यों कर रहे हैं

Prod_dept.prototype = new Product();

क्या यह आवश्यक है? क्योंकि सुपर-कंस्ट्रक्टर में एक कॉल है

Prod_dept()

वैसे भी, इस तरह

Product.call

क्या यह सामान्य व्यवहार से बाहर है? सुपर-कंस्ट्रक्टर के लिए कॉल का उपयोग करना या प्रोटोटाइप चेन का उपयोग करना कब बेहतर है?

function Product(name, value){
  this.name = name;
  if(value >= 1000)
    this.value = 999;
  else
    this.value = value;
}

function Prod_dept(name, value, dept){
  this.dept = dept;
  Product.call(this, name, value);
}

Prod_dept.prototype = new Product();

// since 5 is less than 1000, value is set
cheese = new Prod_dept("feta", 5, "food");

// since 5000 is above 1000, value will be 999
car = new Prod_dept("honda", 5000, "auto");

चीजों को स्पष्ट करने के लिए धन्यवाद


जिस तरह से आपने इसका उपयोग किया है वह लगभग सही है लेकिन आप नए कीवर्ड का उपयोग करके आधार को तुरंत चालू करने के बजाय Object.create () का उपयोग करना चाह सकते हैं (यदि बेस कंस्ट्रक्टर को तर्क की आवश्यकता हो तो मुद्दों का कारण बन सकता है)। मेरे ब्लॉग पर मेरा विवरण है: ncombo.wordpress.com/2013/07/11/…
जॉन

2
यह भी ध्यान दें कि उत्पाद () प्रभावी रूप से दो बार कहा जाता है।
Event_jr

जवाबों:


109

वास्तविक प्रश्न का उत्तर यह है कि आपको दोनों करने की आवश्यकता है:

  • माता-पिता के एक उदाहरण के लिए प्रोटोटाइप सेट करना प्रोटोटाइप चेन (इनहेरिटेंस) को इनिशियलाइज़ करता है, यह केवल एक बार किया जाता है (क्योंकि प्रोटोटाइप ऑब्जेक्ट साझा किया जाता है)।
  • माता-पिता के कंस्ट्रक्टर को कॉल करना ऑब्जेक्ट को खुद को इनिशिअलाइज़ करता है, यह हर इंस्टेंटेशन के साथ किया जाता है (आप इसे बनाते समय हर बार अलग-अलग पैरामीटर पास कर सकते हैं)।

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

क्रिस मॉर्गन का जवाब लगभग पूरा हो चुका है, एक छोटी सी डिटेल (कंस्ट्रक्टर प्रॉपर्टी) गायब है। मुझे इनहेरिटेंस सेटअप करने की विधि सुझाइए।

function extend(base, sub) {
  // Avoid instantiating the base class just to setup inheritance
  // Also, do a recursive merge of two prototypes, so we don't overwrite 
  // the existing prototype, but still maintain the inheritance chain
  // Thanks to @ccnokes
  var origProto = sub.prototype;
  sub.prototype = Object.create(base.prototype);
  for (var key in origProto)  {
     sub.prototype[key] = origProto[key];
  }
  // The constructor property was set wrong, let's fix it
  Object.defineProperty(sub.prototype, 'constructor', { 
    enumerable: false, 
    value: sub 
  });
}

// Let's try this
function Animal(name) {
  this.name = name;
}

Animal.prototype = {
  sayMyName: function() {
    console.log(this.getWordsToSay() + " " + this.name);
  },
  getWordsToSay: function() {
    // Abstract
  }
}

function Dog(name) {
  // Call the parent's constructor
  Animal.call(this, name);
}

Dog.prototype = {
    getWordsToSay: function(){
      return "Ruff Ruff";
    }
}    

// Setup the prototype chain the right way
extend(Animal, Dog);

// Here is where the Dog (and Animal) constructors are called
var dog = new Dog("Lassie");
dog.sayMyName(); // Outputs Ruff Ruff Lassie
console.log(dog instanceof Animal); // true
console.log(dog.constructor); // Dog

कक्षाएं बनाते समय आगे की वाक्य रचना चीनी के लिए मेरा ब्लॉग पोस्ट देखें। http://js-bits.blogspot.com/2010/08/javascript-inheritance-done-right.html

एक्सट-जेएस और http://www.uselesspickles.com/class_library/ से कॉपी की गई तकनीक और https://stackoverflow.com/users/1397311/ccnokes से एक टिप्पणी


6
EcmaScript5 + (सभी आधुनिक ब्राउज़र) में, यदि आप इसे इस तरह परिभाषित करते हैं, तो आप इसे गैर-उपयोग योग्य बना सकते हैं, इस Object.defineProperty(sub.protoype, 'constructor', { enumerable: false, value: sub }); तरह से आपको ठीक वैसा ही "व्यवहार" मिलेगा जब जावास्क्रिप्ट किसी फ़ंक्शन का एक नया उदाहरण बनाता है (निर्माणकर्ता को गणना के रूप में सेट किया जाता है = असत्य अपने आप)
आदिपाबी

2
क्या आप विस्तार विधि को दो लाइनों में सरल नहीं कर सकते थे? अर्थात्: sub.prototype = object.create (base.prototype); sub.prototype.constructor = sub;
17

@ हां, आप कर सकते हैं, जब मैंने पहली बार यह लिखा Object.createथा , तब भी इसका समर्थन नहीं किया गया था ... इसे अपडेट करना। ध्यान दें कि अधिकांश Object.createपॉलीफ़िल्स उस तकनीक का उपयोग करके लागू किए जाते हैं जो मैंने मूल रूप से दिखाया था।
जुआन मेंडेस

1
इसलिए अगर मैं इस मामले में केवल बच्चे की वस्तु और उसके उदाहरण, "डॉग" ऑब्जेक्ट को जोड़ना चाहता था, तो क्या आप इस तरह के विस्तार समारोह के भीतर दो प्रोटोटाइप को मर्ज करेंगे: jsfiddle.net/ccnokes/75f9P ?
ccnokes 3

1
@ elad.chen मैंने जिस उत्तर श्रृंखला में प्रोटोटाइप का वर्णन किया है, मिश्रण आमतौर पर सभी गुणों को एक उदाहरण के लिए कॉपी करते हैं, प्रोटोटाइप के लिए नहीं। देखें stackoverflow.com/questions/7506210/...
जुआन मेंडेस

30

ऐसा करने का आदर्श तरीका यह नहीं है Prod_dept.prototype = new Product();, क्योंकि यह निर्माता को बुलाता है Product। तो आदर्श तरीका यह है कि इसे कंस्ट्रक्टर को छोड़कर, कुछ इस तरह से क्लोन किया जाए:

function Product(...) {
    ...
}
var tmp = function(){};
tmp.prototype = Product.prototype;

function Prod_dept(...) {
    Product.call(this, ...);
}
Prod_dept.prototype = new tmp();
Prod_dept.prototype.constructor = Prod_dept;

फिर सुपर कंस्ट्रक्टर को निर्माण के समय कहा जाता है, जो आप चाहते हैं, क्योंकि तब आप मापदंडों को भी पास कर सकते हैं।

यदि आप Google बंद लाइब्रेरी जैसी चीज़ों को देखते हैं, तो आप देखेंगे कि वे ऐसा कैसे करते हैं।


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

1
@ जुआन: ठीक है, जोड़ने के लिए अद्यतन किया गया Prod_dept.prototype.constructor = Prod_dept;
क्रिस मॉर्गन

@ क्रिसमोरन मुझे आपके नमूने में अंतिम पंक्ति को समझने में परेशानी हो रही है Prod_dept.prototype.constructor = Prod_dept;:। सबसे पहले, इसकी आवश्यकता क्यों है और Prod_deptइसके बजाय यह क्यों इंगित करता है Product?
लेस्से क्रिस्टियनसेन

1
@ LasseChristiansen-sw_lasse: Prod_dept.prototypeवह है जिसके आउटपुट के प्रोटोटाइप के रूप में उपयोग किया जाएगा new Prod_dept()। (आमतौर पर यह प्रोटोटाइप के रूप में उपलब्ध है instance.__proto__, हालांकि यह एक कार्यान्वयन विवरण है।) इस कारण से constructor- यह भाषा का एक मानक हिस्सा है और इसलिए इसे स्थिरता के लिए प्रदान किया जाना चाहिए; यह डिफ़ॉल्ट रूप से सही है, लेकिन क्योंकि हम प्रोटोटाइप को पूरी तरह से बदल रहे हैं, हमें फिर से सही मान असाइन करना होगा, या कुछ चीजें समझदार नहीं होंगी (इस मामले में, इसका मतलब होगा कि एक Prod_deptउदाहरण होगा this.constructor == Product, जो खराब है)।
क्रिस मॉर्गन

6

यदि आपने जावास्क्रिप्ट में ऑब्जेक्ट ओरिएंटेड प्रोग्रामिंग की है, तो आप जानेंगे कि आप निम्नानुसार एक क्लास बना सकते हैं:

Person = function(id, name, age){
    this.id = id;
    this.name = name;
    this.age = age;
    alert('A new person has been accepted');
}

अब तक हमारे वर्ग के व्यक्ति के पास केवल दो गुण हैं और हम इसे कुछ तरीके देने जा रहे हैं। ऐसा करने का एक साफ तरीका अपनी 'प्रोटोटाइप' ऑब्जेक्ट का उपयोग करना है। जावास्क्रिप्ट 1.1 से शुरू होकर प्रोटोटाइप ऑब्जेक्ट को जावास्क्रिप्ट में पेश किया गया था। यह एक अंतर्निहित वस्तु है जो किसी वस्तु के सभी उदाहरणों में कस्टम गुण और विधियों को जोड़ने की प्रक्रिया को सरल बनाता है। आइए इसकी कक्षा में 'प्रोटोटाइप' ऑब्जेक्ट का उपयोग करते हुए 2 तरीकों को इस प्रकार जोड़ें:

Person.prototype = {
    /** wake person up */
    wake_up: function() {
        alert('I am awake');
    },

    /** retrieve person's age */
    get_age: function() {
        return this.age;
    }
}

अब हमने अपने वर्ग के व्यक्ति को परिभाषित किया है। क्या होगा अगर हम प्रबंधक नामक एक अन्य वर्ग को परिभाषित करना चाहते हैं जो व्यक्ति से कुछ गुण प्राप्त करता है। इस सभी गुणों को फिर से परिभाषित करने का कोई मतलब नहीं है जब हम अपने प्रबंधक वर्ग को परिभाषित करते हैं, तो हम इसे वर्ग व्यक्ति से विरासत में प्राप्त कर सकते हैं। जावास्क्रिप्ट ने विरासत में नहीं बनाया है, लेकिन हम विरासत को लागू करने के लिए एक तकनीक का उपयोग इस प्रकार कर सकते हैं:

Inheritance_Manager = {};// हम एक विरासत प्रबंधक वर्ग बनाते हैं (नाम मनमाना है)

अब हम अपने वंशानुक्रम वर्ग को एक विधि देते हैं जिसे विस्तार कहते हैं जो बेसक्लास और सबक्लास तर्कों को लेता है। विस्तार विधि के भीतर, हम इनहेरिटेंस फंक्शन इनहेरिटेंस () {} नामक एक आंतरिक वर्ग बनाएंगे। हम इस आंतरिक वर्ग का उपयोग क्यों कर रहे हैं इसका कारण बेसक्लास और सबक्लास प्रोटोटाइप के बीच भ्रम से बचना है। अगला हम निम्नलिखित कोड के साथ बेसक्लास प्रोटोटाइप के रूप में अपने उत्तराधिकार वर्ग बिंदु का प्रोटोटाइप बनाते हैं: उत्तराधिकार।प्रोटाइप = बेसक्लास। प्रोटोटाइप; फिर हम उप-प्रोटो प्रोटोटाइप में इनहेरिटेंस प्रोटोटाइप को निम्नानुसार कॉपी करते हैं: सबक्लास.प्रोटोटाइप = नई विरासत (); अगली बात यह है कि हमारे उप-निर्माण के लिए कंस्ट्रक्टर को इस प्रकार निर्दिष्ट करना है: सबक्लास.प्रोटाइप.कंस्ट्रक्टर = उपक्लास; एक बार हमारे सबक्लास प्रोटोटाइप के साथ समाप्त होने के बाद, हम कुछ बेस क्लास पॉइंटर्स सेट करने के लिए कोड की अगली दो पंक्तियों को निर्दिष्ट कर सकते हैं।

subClass.baseConstructor = baseClass;
subClass.superClass = baseClass.prototype;

यहाँ हमारे विस्तार समारोह के लिए पूर्ण कोड है:

Inheritance_Manager.extend = function(subClass, baseClass) {
    function inheritance() { }
    inheritance.prototype = baseClass.prototype;
    subClass.prototype = new inheritance();
    subClass.prototype.constructor = subClass;
    subClass.baseConstructor = baseClass;
    subClass.superClass = baseClass.prototype;
}

अब जब हमने अपनी विरासत को लागू कर दिया है, तो हम अपनी कक्षाओं का विस्तार करने के लिए इसका उपयोग करना शुरू कर सकते हैं। इस मामले में हम अपने व्यक्तिगत वर्ग को एक प्रबंधक वर्ग में निम्नानुसार विस्तारित करने जा रहे हैं:

हम प्रबंधक वर्ग को परिभाषित करते हैं

Manager = function(id, name, age, salary) {
    Person.baseConstructor.call(this, id, name, age);
    this.salary = salary;
    alert('A manager has been registered.');
}

हम इसे व्यक्तिगत रूप बनाते हैं

Inheritance_Manager.extend(Manager, Person);

यदि आपने गौर किया है, तो हमने अपने इनहेरिटैंस_मैनेजर क्लास की विस्तार विधि को कॉल किया है और हमारे मामले में उप-प्रबंधक और उसके बाद बेसक्लास पर्सन को पास किया है। ध्यान दें कि यहां आदेश बहुत महत्वपूर्ण है। यदि आप उन्हें अदला-बदली करते हैं, तो विरासत आपके अनुसार काम नहीं करेगी। यह भी ध्यान दें कि आपको वास्तव में हमारे उपवर्ग को परिभाषित करने से पहले इस विरासत को निर्दिष्ट करने की आवश्यकता होगी। अब हम अपने उपवर्ग को परिभाषित करते हैं:

हम नीचे दिए गए तरीकों के रूप में और अधिक तरीके जोड़ सकते हैं। हमारे प्रबंधक वर्ग के पास हमेशा व्यक्ति वर्ग में परिभाषित तरीके और गुण होंगे क्योंकि यह उससे विरासत में मिला है।

Manager.prototype.lead = function(){
   alert('I am a good leader');
}

अब इसका परीक्षण करने के लिए, हम दो ऑब्जेक्ट बनाते हैं, एक क्लास पर्सन से और एक विरासत क्लास मैनेजर से:

var p = new Person(1, 'Joe Tester', 26);
var pm = new Manager(1, 'Joe Tester', 26, '20.000');

फुल कोड और अधिक टिप्पणियाँ प्राप्त करने के लिए स्वतंत्र महसूस करें: http://www.cyberminds.co.uk/blog/articles/how-to-implement-javascript-inheritance.aspx

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