मुझे पता है कि यह उत्तर 3 साल देर से है, लेकिन मुझे वास्तव में लगता है कि वर्तमान उत्तर इस बारे में पर्याप्त जानकारी नहीं देते हैं कि प्रोटोटाइप विरासत में शास्त्रीय विरासत से बेहतर कैसे है ।
पहले आइए देखें कि प्रोटोटाइप प्रोग्रामों के बचाव में जावास्क्रिप्ट प्रोग्रामर राज्य सबसे आम तर्क देते हैं (मैं इन तर्कों को उत्तर के वर्तमान पूल से ले रहा हूं):
- यह आसान है।
- यह शक्तिशाली है।
- यह छोटे, कम निरर्थक कोड की ओर जाता है।
- यह गतिशील है और इसलिए यह गतिशील भाषाओं के लिए बेहतर है।
अब ये तर्क सभी मान्य हैं, लेकिन किसी ने भी इसकी व्याख्या करने की जहमत नहीं उठाई। यह एक बच्चे को बताने जैसा है कि मैथ्स पढ़ना महत्वपूर्ण है। यकीन है कि यह है, लेकिन बच्चा निश्चित रूप से परवाह नहीं करता है; और आप यह कहकर मैथ्स जैसा बच्चा नहीं बना सकते कि यह महत्वपूर्ण है।
मुझे लगता है कि प्रोटोटाइप विरासत के साथ समस्या यह है कि इसे जावास्क्रिप्ट के परिप्रेक्ष्य से समझाया गया है। मुझे जावास्क्रिप्ट बहुत पसंद है, लेकिन जावास्क्रिप्ट में प्रोटोटाइप की विरासत गलत है। शास्त्रीय विरासत के विपरीत, प्रोटोटाइप विरासत के दो पैटर्न हैं:
- प्रोटोटाइप इनहेरिटेंस का प्रोटोटाइप पैटर्न।
- प्रोटोटाइप इनहेरिटेंस का निर्माता पैटर्न।
दुर्भाग्य से जावास्क्रिप्ट प्रोटोोटाइपल इनहेरिटेंस के कंस्ट्रक्टर पैटर्न का उपयोग करता है। इसका कारण यह है कि जब जावास्क्रिप्ट बनाया गया था, ब्रेंडन ईच (जेएस के निर्माता) चाहते थे कि यह जावा की तरह दिखे (जिसमें शास्त्रीय विरासत है):
और हम इसे जावा के एक छोटे भाई के रूप में धकेल रहे थे, उस समय विजुअल बेसिक जैसी पूरक भाषा के रूप में माइक्रोसॉफ्ट के भाषा परिवारों में C ++ था।
यह बुरा है क्योंकि जब लोग जावास्क्रिप्ट में कंस्ट्रक्टर का उपयोग करते हैं तो वे अन्य कंस्ट्रक्टरों से विरासत में मिले कंस्ट्रक्टर के बारे में सोचते हैं। ये गलत है। प्रोटोटाइप में विरासत की वस्तुएं अन्य वस्तुओं से विरासत में मिली हैं। कंस्ट्रक्टर कभी भी तस्वीर में नहीं आते। यह वही है जो ज्यादातर लोगों को भ्रमित करता है।
जावा जैसी भाषाओं के लोग, जिनमें शास्त्रीय विरासत होती है, और भी अधिक भ्रमित हो जाते हैं क्योंकि यद्यपि निर्माता वर्ग की तरह दिखते हैं, वे कक्षाओं की तरह व्यवहार नहीं करते हैं। जैसा कि डगलस क्रॉकफोर्ड ने कहा:
यह अप्रत्यक्ष रूप से भाषा को प्रशिक्षित प्रशिक्षित प्रोग्रामरों के लिए अधिक परिचित लगता है, लेकिन ऐसा करने में विफल रहा, जैसा कि हम बहुत कम राय से देख सकते हैं कि जावा प्रोग्रामर जावास्क्रिप्ट के हैं। जावास्क्रिप्ट का निर्माता पैटर्न शास्त्रीय भीड़ के लिए अपील नहीं करता था। इसने जावास्क्रिप्ट के वास्तविक प्रोटोटाइप स्वभाव को भी अस्पष्ट कर दिया। नतीजतन, बहुत कम प्रोग्रामर हैं जो भाषा का प्रभावी ढंग से उपयोग करना जानते हैं।
ये लो। सीधे घोड़े के मुंह से।
सच्चा प्रोटोटाइप इनहेरिटेंस
प्रोटोटाइप इनहेरिटेंस सभी वस्तुओं के बारे में है। वस्तुएं अन्य वस्तुओं से गुण प्राप्त करती हैं। यही सब है इसके लिए। प्रोटोटाइप इनहेरिटेंस का उपयोग करके ऑब्जेक्ट बनाने के दो तरीके हैं:
- एकदम नई वस्तु बनाएँ।
- किसी मौजूदा ऑब्जेक्ट को क्लोन करें और उसका विस्तार करें।
नोट: जावास्क्रिप्ट एक वस्तु क्लोन करने के दो तरीके प्रदान करता है - प्रतिनिधिमंडल और संयोजन । इसके बाद, मैं "क्लोन" शब्द का उपयोग विशेष रूप से प्रतिनिधिमंडल के माध्यम से विरासत का उल्लेख करने के लिए करूँगा, और शब्द "कॉपी" विशेष रूप से संघनन के माध्यम से विरासत का उल्लेख करने के लिए।
खूब बात की। आइए कुछ उदाहरण देखें। कहो मेरे पास त्रिज्या का एक चक्र है 5
:
var circle = {
radius: 5
};
हम इसके दायरे से क्षेत्र और वृत्त की परिधि की गणना कर सकते हैं:
circle.area = function () {
var radius = this.radius;
return Math.PI * radius * radius;
};
circle.circumference = function () {
return 2 * Math.PI * this.radius;
};
अब मैं त्रिज्या का एक और चक्र बनाना चाहता हूं 10
। ऐसा करने का एक तरीका होगा:
var circle2 = {
radius: 10,
area: circle.area,
circumference: circle.circumference
};
हालाँकि जावास्क्रिप्ट एक बेहतर तरीका प्रदान करता है - प्रतिनिधिमंडल । ऐसा करने के लिए Object.create
फ़ंक्शन का उपयोग किया जाता है:
var circle2 = Object.create(circle);
circle2.radius = 10;
बस इतना ही। आपने अभी जावास्क्रिप्ट में प्रोटोटाइप इनहेरिटेंस किया है। इतना आसान नहीं था? आप एक ऑब्जेक्ट लेते हैं, इसे क्लोन करते हैं, आपको जो कुछ भी ज़रूरत है उसे बदल दें और हे प्रिस्टो - आपने अपने आप को एक नया ऑब्जेक्ट प्राप्त किया।
अब आप पूछ सकते हैं, "यह कैसे सरल है? हर बार जब मैं एक नया सर्कल बनाना चाहता हूं तो मुझे क्लोन करना होगा circle
और मैन्युअल रूप से इसे एक त्रिज्या असाइन करना होगा"। अच्छी तरह से समाधान आप के लिए भारी उठाने के लिए एक समारोह का उपयोग करने के लिए है:
function createCircle(radius) {
var newCircle = Object.create(circle);
newCircle.radius = radius;
return newCircle;
}
var circle2 = createCircle(10);
वास्तव में आप इस सभी को एक एकल वस्तु शाब्दिक में जोड़ सकते हैं:
var circle = {
radius: 5,
create: function (radius) {
var circle = Object.create(this);
circle.radius = radius;
return circle;
},
area: function () {
var radius = this.radius;
return Math.PI * radius * radius;
},
circumference: function () {
return 2 * Math.PI * this.radius;
}
};
var circle2 = circle.create(10);
जावास्क्रिप्ट में प्रोटोटाइप इनहेरिटेंस
यदि आप उपरोक्त कार्यक्रम में देखते हैं कि create
फ़ंक्शन एक क्लोन बनाता है circle
, radius
तो उसे एक नया असाइन करता है और फिर उसे वापस लौटाता है। यह वही है जो एक निर्माणकर्ता जावास्क्रिप्ट में करता है:
function Circle(radius) {
this.radius = radius;
}
Circle.prototype.area = function () {
var radius = this.radius;
return Math.PI * radius * radius;
};
Circle.prototype.circumference = function () {
return 2 * Math.PI * this.radius;
};
var circle = new Circle(5);
var circle2 = new Circle(10);
जावास्क्रिप्ट में कंस्ट्रक्टर पैटर्न उल्टा प्रोटोटाइप पैटर्न है। एक ऑब्जेक्ट बनाने के बजाय आप एक कंस्ट्रक्टर बनाते हैं। new
कीवर्ड बांधता this
का क्लोन करने के लिए निर्माता के अंदर सूचक prototype
निर्माता के।
भ्रामक लगता है? ऐसा इसलिए है क्योंकि जावास्क्रिप्ट में कंस्ट्रक्टर पैटर्न अनावश्यक रूप से चीजों को जटिल करता है। यह वही है जो अधिकांश प्रोग्रामर को समझना मुश्किल है।
अन्य वस्तुओं से विरासत में मिली वस्तुओं के बारे में सोचने के बजाय, वे सोचते हैं कि कंस्ट्रक्टर अन्य कंस्ट्रक्टर्स से विरासत में मिला है और फिर पूरी तरह से भ्रमित हो गए हैं।
जावास्क्रिप्ट में निर्माण पैटर्न से बचा जाना चाहिए क्यों अन्य कारणों का एक पूरा गुच्छा है। आप उनके बारे में मेरे ब्लॉग पोस्ट में यहाँ पढ़ सकते हैं: कन्स्ट्रक्टर्स बनाम प्रोटोटाइप
तो शास्त्रीय विरासत से अधिक प्रोटोटाइप विरासत के लाभ क्या हैं? चलो फिर से सबसे आम तर्कों के माध्यम से चलते हैं, और बताते हैं कि क्यों ।
1. प्रोटोटाइप इनहेरिटेंस सरल है
सीएमएस ने अपने जवाब में कहा:
मेरी राय में, प्रोटोटाइप विरासत का प्रमुख लाभ इसकी सादगी है।
आइए विचार करें कि हमने अभी क्या किया। हमने एक ऐसी वस्तु बनाई, circle
जिसकी त्रिज्या थी 5
। फिर हमने इसे क्लोन किया और क्लोन को एक त्रिज्या दिया 10
।
इसलिए हमें केवल दो चीज़ों की ज़रूरत है जो कि प्रोटोटाइप इनहेरिटेंस काम करें:
- एक नई वस्तु बनाने का एक तरीका (जैसे वस्तु शाब्दिक)।
- मौजूदा वस्तु (जैसे
Object.create
) का विस्तार करने का एक तरीका ।
इसके विपरीत शास्त्रीय विरासत अधिक जटिल है। शास्त्रीय विरासत में आपके पास:
- क्लासेस।
- वस्तु।
- इंटरफेस।
- सार वर्ग।
- अंतिम कक्षाएं।
- वर्चुअल बेस क्लासेस।
- कंस्ट्रक्टर्स।
- विनाशकर्ता।
तुम्हें नया तरीका मिल गया है। मुद्दा यह है कि प्रोटोटाइप की विरासत को समझना आसान है, लागू करना आसान है, और इसके बारे में तर्क करना आसान है।
जैसा कि स्टीव येजे ने इसे अपने क्लासिकल ब्लॉग पोस्ट " पोर्ट्रेट ऑफ एन एनबीबी " में रखा है:
मेटाडेटा किसी भी तरह का वर्णन या मॉडल है। आपके कोड में टिप्पणियाँ अभिकलन का सिर्फ एक प्राकृतिक-भाषा विवरण हैं। मेटाडेटा मेटा-डेटा क्या बनाता है यह कड़ाई से आवश्यक नहीं है। यदि मेरे पास कुछ वंशावली कागजी कार्रवाई के साथ एक कुत्ता है, और मैं कागजी कार्रवाई खो देता हूं, तो मेरे पास अभी भी पूरी तरह से वैध कुत्ता है।
उसी अर्थ में कक्षाएं सिर्फ मेटा-डेटा हैं। वंशानुक्रम के लिए वर्गों की कड़ाई से आवश्यकता नहीं है। हालांकि कुछ लोग (आमतौर पर n00bs) के साथ काम करने के लिए कक्षाएं अधिक आरामदायक पाते हैं। यह उन्हें सुरक्षा की झूठी भावना देता है।
वैसे, हम यह भी जानते हैं कि स्थैतिक प्रकार केवल मेटाडेटा हैं। वे दो प्रकार के पाठकों पर लक्षित एक विशेष प्रकार की टिप्पणी कर रहे हैं: प्रोग्रामर और संकलनकर्ता। स्टेटिक प्रकार गणना के बारे में एक कहानी बताते हैं, संभवतः दोनों पाठक समूहों को कार्यक्रम के इरादे को समझने में मदद करने के लिए। लेकिन स्थैतिक प्रकारों को रनटाइम पर फेंक दिया जा सकता है, क्योंकि अंत में वे केवल शैलीगत टिप्पणियां हैं। वे पेडिग्री पेपरवर्क की तरह हैं: यह उनके कुत्ते के बारे में एक निश्चित असुरक्षित व्यक्तित्व प्रकार को खुश कर सकता है, लेकिन कुत्ता निश्चित रूप से परवाह नहीं करता है।
जैसा कि मैंने पहले कहा, कक्षाएं लोगों को सुरक्षा का गलत अर्थ देती हैं। उदाहरण के लिए आप NullPointerException
जावा में बहुत अधिक एस प्राप्त करते हैं तब भी जब आपका कोड पूरी तरह से सुपाठ्य हो। मुझे लगता है कि शास्त्रीय विरासत आमतौर पर प्रोग्रामिंग के रास्ते में मिलती है, लेकिन शायद यह सिर्फ जावा है। पायथन में एक अद्भुत शास्त्रीय विरासत प्रणाली है।
2. प्रोटोटाइप इनहेरिटेंस शक्तिशाली है
शास्त्रीय पृष्ठभूमि से आने वाले अधिकांश प्रोग्रामर्स का तर्क है कि शास्त्रीय विरासत प्रोटोटाइप की विरासत की तुलना में अधिक शक्तिशाली है क्योंकि इसमें निम्नलिखित हैं:
- निजी चर।
- एकाधिक वंशानुक्रम।
यह दावा झूठा है। हम पहले से ही जानते हैं कि जावास्क्रिप्ट निजी चर को बंद करने के माध्यम से समर्थन करता है , लेकिन कई विरासत के बारे में क्या? जावास्क्रिप्ट में वस्तुओं का केवल एक ही प्रोटोटाइप है।
सच्चाई यह है कि प्रोटोोटाइपल इनहेरिटेंस कई प्रोटोटाइप से विरासत में मिलने का समर्थन करती है। प्रोटोटाइप इनहेरिटेंस का सीधा सा मतलब है कि एक वस्तु दूसरी वस्तु से विरासत में मिली। वास्तव में प्रोटोटाइप विरासत को लागू करने के दो तरीके हैं :
- प्रत्यायोजन या विभेदक वंशानुक्रम
- क्लोनिंग या कॉन्टेनेटिव इनहेरिटेंस
हां जावास्क्रिप्ट केवल वस्तुओं को एक दूसरे ऑब्जेक्ट को सौंपने की अनुमति देता है। हालांकि यह आपको वस्तुओं की एक मनमानी संख्या के गुणों को कॉपी करने की अनुमति देता है। उदाहरण के लिए _.extend
बस यही करता है।
बेशक कई प्रोग्रामर इसे सही उत्तराधिकार नहीं मानते हैं क्योंकि instanceof
और isPrototypeOf
अन्यथा कहते हैं। हालाँकि यह आसानी से हर वस्तु पर प्रोटोटाइप की एक सरणी को संग्रहीत करके सुधारा जा सकता है जो एक प्रोटोटाइप से विरासत में प्राप्त होता है:
function copyOf(object, prototype) {
var prototypes = object.prototypes;
var prototypeOf = Object.isPrototypeOf;
return prototypes.indexOf(prototype) >= 0 ||
prototypes.some(prototypeOf, prototype);
}
इसलिए प्रोटोटाइप विरासत में शास्त्रीय विरासत की तरह ही शक्तिशाली है। वास्तव में यह शास्त्रीय विरासत की तुलना में बहुत अधिक शक्तिशाली है क्योंकि प्रोटोटाइप विरासत में आप विभिन्न गुणों से हटने के लिए कौन से गुणों की नकल कर सकते हैं और कौन से गुण।
शास्त्रीय विरासत में यह असंभव है (या कम से कम बहुत मुश्किल) यह चुनने के लिए कि आपको कौन से गुण विरासत में प्राप्त करना चाहते हैं। वे हीरे की समस्या को हल करने के लिए वर्चुअल बेस क्लास और इंटरफेस का इस्तेमाल करते हैं ।
हालांकि जावास्क्रिप्ट में आपको सबसे अधिक संभावना हीरे की समस्या के बारे में कभी नहीं सुनाई देगी क्योंकि आप वास्तव में उन गुणों को नियंत्रित कर सकते हैं जिन्हें आप विरासत में लेना चाहते हैं और जिसमें से प्रोटोटाइप हैं।
3. प्रोटोटाइप इनहेरिटेंस कम निरर्थक है
इस बिंदु को समझाना थोड़ा अधिक कठिन है क्योंकि शास्त्रीय विरासत में आवश्यक रूप से अधिक अनावश्यक कोड नहीं होता है। वास्तव में विरासत, चाहे शास्त्रीय या प्रोटोटाइप, कोड में अतिरेक को कम करने के लिए उपयोग किया जाता है।
एक तर्क यह हो सकता है कि शास्त्रीय विरासत के साथ अधिकांश प्रोग्रामिंग भाषाओं को सांख्यिकीय रूप से टाइप किया गया है और उपयोगकर्ता को स्पष्ट रूप से प्रकारों के विपरीत (हास्केल के विपरीत जो स्थैतिक टाइपिंग निहित है) की आवश्यकता होती है। इसलिए यह अधिक क्रिया कोड की ओर जाता है।
जावा इस व्यवहार के लिए कुख्यात है। मुझे स्पष्ट रूप से बॉब Nystrom को प्रैट परसर्स के बारे में अपने ब्लॉग पोस्ट में निम्नलिखित उपाख्यानों का उल्लेख करना याद है :
आपको जावा की "कृपया इसे यहाँ की नौकरशाही के चतुष्कोणीय स्तर पर हस्ताक्षर करें" से प्यार होगा।
फिर से, मुझे लगता है कि जावा केवल इतना ही चूसता है।
एक मान्य तर्क यह है कि सभी भाषाओं में शास्त्रीय विरासत का समर्थन नहीं होता है जो कई विरासतों का समर्थन करती हैं। फिर से जावा का ख्याल आता है। हां जावा में इंटरफेस है, लेकिन यह पर्याप्त नहीं है। कभी-कभी आपको वास्तव में कई विरासतों की आवश्यकता होती है।
चूंकि प्रोटोटाइप वंशानुक्रम एकाधिक वंशानुक्रम के लिए अनुमति देता है, कोड जिसे कई वंशानुक्रम की आवश्यकता होती है वह कम निरर्थक होता है यदि एक भाषा के बजाय प्रोटोटाइप विरासत का उपयोग करके लिखा जाता है जिसमें शास्त्रीय वंशानुक्रम होता है लेकिन कोई एकाधिक वंशानुक्रम नहीं होता है।
4. प्रोटोटाइप इनहेरिटेंस गतिशील है
प्रोटोटाइप विरासत के सबसे महत्वपूर्ण लाभों में से एक यह है कि आप प्रोटोटाइप के निर्माण के बाद उनमें नए गुण जोड़ सकते हैं। यह आपको एक प्रोटोटाइप में नए तरीके जोड़ने की अनुमति देता है जो स्वचालित रूप से उन सभी वस्तुओं को उपलब्ध कराया जाएगा जो उस प्रोटोटाइप को सौंपते हैं।
शास्त्रीय वंशानुक्रम में यह संभव नहीं है क्योंकि एक बार एक वर्ग बनाने के बाद आप इसे रनटाइम में संशोधित नहीं कर सकते। शास्त्रीय विरासत के मुकाबले प्रोटोटाइप विरासत का शायद यह सबसे बड़ा लाभ है, और यह शीर्ष पर होना चाहिए था। हालांकि मुझे अंत के लिए सर्वश्रेष्ठ बचत करना पसंद है।
निष्कर्ष
प्रोटोटाइप वंशानुक्रम मायने रखता है। जावास्क्रिप्ट प्रोग्रामर्स को शिक्षित करना महत्वपूर्ण है क्यों कि प्रोटोटाइप इनहेरिटेंस के प्रोटोटाइप पैटर्न के पक्ष में प्रोटोटाइप उत्तराधिकार के निर्माता पैटर्न को छोड़ देना चाहिए।
हमें जावास्क्रिप्ट को सही ढंग से पढ़ाना शुरू करने की आवश्यकता है और इसका मतलब है कि नए प्रोग्रामर को दिखाना है कि कंस्ट्रक्टर पैटर्न के बजाय प्रोटोटाइप पैटर्न का उपयोग करके कोड कैसे लिखना है।
न केवल प्रोटोटाइप पैटर्न का उपयोग करके प्रोटोटाइप विरासत की व्याख्या करना आसान होगा, बल्कि यह बेहतर प्रोग्रामर भी बनाएगा।
अगर आपको यह उत्तर पसंद आया है तो आपको " Why Prototypal Inheritance Matters " पर मेरा ब्लॉग पोस्ट भी पढ़ना चाहिए । मेरा विश्वास करो, आप निराश नहीं होंगे।