2011-01-19 24 views
73

का उपयोग करने के लिए जब मैं समझना चाहता हूं कि जेएस में प्रोटोटाइप विधियों का उपयोग करना उचित है। क्या उन्हें हमेशा इस्तेमाल किया जाना चाहिए? या ऐसे मामले हैं जहां उनका उपयोग करना पसंद नहीं है और/या प्रदर्शन जुर्माना लगाता है?जावास्क्रिप्ट प्रोटोटाइप

जेएस में नेमस्पेसिंग के लिए सामान्य तरीकों पर इस साइट के आसपास खोज करने में, ऐसा लगता है कि अधिकांश गैर-प्रोटोटाइप आधारित कार्यान्वयन का उपयोग करते हैं: बस किसी ऑब्जेक्ट या फ़ंक्शन ऑब्जेक्ट का उपयोग किसी नामस्थान को समाहित करने के लिए करते हैं।

कक्षा-आधारित भाषा से आना, समानांतर कोशिश करना और आकर्षित करना मुश्किल नहीं है और लगता है कि प्रोटोटाइप "कक्षाएं" की तरह हैं और मैंने निर्दिष्ट नामस्थान कार्यान्वयन स्थिर तरीकों की तरह हैं।

उत्तर

115

प्रोटोटाइप एक अनुकूलन हैं।

उनका उपयोग करने का एक अच्छा उदाहरण jQuery लाइब्रेरी है। हर बार जब आप $('.someClass') का उपयोग करके एक jQuery ऑब्जेक्ट प्राप्त करते हैं, तो उस ऑब्जेक्ट में दर्जनों "विधियां" होती हैं। पुस्तकालय है कि प्राप्त कर सकते थे एक वस्तु लौटाएँ:

return { 
    show: function() { ... }, 
    hide: function() { ... }, 
    css: function() { ... }, 
    animate: function() { ... }, 
    // etc... 
}; 

लेकिन मतलब यह होगा कि कि स्मृति में हर jQuery वस्तु नामित स्लॉट, एक ही तरीके से युक्त अधिक से अधिक के दर्जनों होगा।

इसके बजाय, उन विधियों को प्रोटोटाइप पर परिभाषित किया गया है और सभी jQuery ऑब्जेक्ट्स प्रोटोटाइप "उत्तराधिकारी" हैं ताकि वे सभी विधियों को बहुत कम रनटाइम लागत पर प्राप्त कर सकें।

jQuery को यह सही तरीके से प्राप्त करने का एक महत्वपूर्ण हिस्सा यह है कि यह प्रोग्रामर से छिपा हुआ है। इसका पूरी तरह से अनुकूलन का इलाज किया जाता है, न कि लाइब्रेरी का उपयोग करते समय आपको चिंता करने की ज़रूरत है।

जावास्क्रिप्ट के साथ समस्या यह है कि नग्न रचनाकार कार्यों को कॉलर को new के साथ उपसर्ग करने के लिए याद रखने की आवश्यकता होती है या अन्यथा वे आमतौर पर काम नहीं करते हैं। इसके लिए कोई अच्छा कारण नहीं है। jQuery को सामान्य कार्य के पीछे उस बकवास को छिपाकर सही हो जाता है, $, इसलिए आपको यह ध्यान रखना नहीं है कि ऑब्जेक्ट्स कैसे कार्यान्वित किए जाते हैं।

ताकि आप आसानी से निर्दिष्ट प्रोटोटाइप के साथ ऑब्जेक्ट बना सकें, ईसीएमएस्क्रिप्ट 5 में मानक फ़ंक्शन Object.create शामिल है। इसके बारे में एक बहुत सरलीकृत संस्करण इस प्रकार दिखाई देगा:

Object.create = function(prototype) { 
    var Type = function() {}; 
    Type.prototype = prototype; 
    return new Type(); 
}; 

यह सिर्फ एक निर्माता समारोह लेखन और फिर new के साथ बुलाने की दर्द का ख्याल रखता है।

आप प्रोटोटाइप से बचेंगे?

उपयोगी तुलना ओओ भाषाओं जैसे जावा और सी # के साथ उपयोगी तुलना है।

  • इंटरफ़ेस विरासत है, जहां आप implement एक interface ऐसा है कि वर्ग इंटरफ़ेस के हर सदस्य के लिए अपनी अनूठी कार्यान्वयन प्रदान करता है: ये विरासत के दो प्रकार का समर्थन है।
  • कार्यान्वयन विरासत है, जहां आप extend एक class कि कुछ तरीकों के डिफ़ॉल्ट कार्यान्वयन प्रदान करता है।

जावास्क्रिप्ट में, प्रोटोटाइपिकल विरासत कार्यान्वयन विरासत का एक प्रकार है। तो उन परिस्थितियों में जहां (सी # या जावा में) आप मूल वर्ग प्राप्त करने के लिए बेस क्लास से व्युत्पन्न होते, जिसे आप ओवरराइड के माध्यम से छोटे बदलाव करते हैं, फिर जावास्क्रिप्ट में, प्रोटोटाइपिकल विरासत समझ में आता है।

हालांकि, यदि आप ऐसी परिस्थिति में हैं जहां आपने सी # या जावा में इंटरफेस का उपयोग किया होगा, तो आपको जावास्क्रिप्ट में किसी विशेष भाषा सुविधा की आवश्यकता नहीं है। ,

var duck = { 
    quack: function() { ... } 
}; 

duck.quack(); // we're satisfied it's a duck! 

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

और वास्तव में, it has been suggested by many people that implementation inheritance is evil। यही है, यदि किसी प्रकार के लिए कुछ सामान्य संचालन हैं, तो शायद यह स्पष्ट है कि उन्हें आधार/सुपर क्लास में नहीं रखा गया है, लेकिन इसके बजाय कुछ मॉड्यूल में सामान्य कार्यों के रूप में उजागर किया गया है, जिसके लिए आप ऑब्जेक्ट पास करते हैं आप उन्हें चालू करना चाहते हैं।

+1

अच्छी व्याख्या। तो क्या आप इस बात से सहमत होंगे कि, चूंकि आप प्रोटोटाइप को ऑप्टिमाइज़ेशन मानते हैं, तो इनका उपयोग हमेशा आपके कोड को बेहतर बनाने के लिए किया जा सकता है? मैं सोच रहा हूं कि ऐसे मामले हैं जहां प्रोटोटाइप का उपयोग करना समझ में नहीं आता है, या वास्तव में प्रदर्शन जुर्माना लगाता है। – opl

+0

आपके अनुवर्ती अनुवर्ती में, आप उल्लेख करते हैं कि "यह इस बात पर निर्भर करता है कि आप प्रत्येक प्रकार के कितने आवंटन आवंटित करते हैं"। लेकिन उदाहरण के लिए आप प्रोटोटाइप का उपयोग नहीं कर रहे हैं। एक उदाहरण आवंटित करने की धारणा कहां है (क्या आप अभी भी "नया" उपयोग कर रहे हैं)? इसके अलावा: क्वैक विधि का पैरामीटर था - क्या duck.quack (param) का प्रत्येक आमंत्रण स्मृति में एक नई ऑब्जेक्ट बनने का कारण बनता है (शायद इसका अप्रासंगिक अगर उसके पास पैरामीटर है या नहीं)? – opl

+3

** 1। ** मेरा मतलब था कि अगर एक प्रकार के बतख की बड़ी संख्या में उदाहरण थे, तो उदाहरण को संशोधित करना समझ जाएगा ताकि 'क्वाक' फ़ंक्शन प्रोटोटाइप में हो, जिसमें कई बतख उदाहरण जुड़े हुए हैं। ** 2। ** ऑब्जेक्ट शाब्दिक वाक्यविन्यास '{...} 'एक उदाहरण बनाता है (इसके साथ' नया 'उपयोग करने की आवश्यकता नहीं है)। ** 3। ** किसी भी फंक्शन को कॉल करना जेएस स्मृति में कम से कम एक ऑब्जेक्ट बनने का कारण बनता है - इसे 'तर्क' ऑब्जेक्ट कहा जाता है और कॉल में दिए गए तर्कों को संग्रहीत करता है: https://developer.mozilla.org/en/JavaScript/संदर्भ/functions_and_function_scope/तर्क –

11

किसी प्रोटोटाइप ऑब्जेक्ट पर फ़ंक्शंस रखें जब आप किसी विशेष प्रकार की ऑब्जेक्ट की बहुत सारी प्रतियां बनाने जा रहे हैं और उन्हें सभी को सामान्य व्यवहार साझा करने की आवश्यकता है। ऐसा करके, आप प्रत्येक कार्य की केवल एक प्रति प्राप्त करके कुछ स्मृति सहेज लेंगे, लेकिन यह केवल सबसे सरल लाभ है।

प्रोटोटाइप ऑब्जेक्ट्स या विधियों को जोड़ने पर विधियों को बदलना, तत्काल प्रकार के सभी उदाहरणों की प्रकृति को तुरंत बदलता है।

अब बिल्कुल क्यों आप इन सब बातों करना चाहते हैं ज्यादातर अपने खुद के आवेदन डिजाइन की एक समारोह है, और चीजों की तरह आप क्लाइंट-साइड कोड में क्या करने की जरूरत। (एक पूरी तरह से अलग कहानी एक सर्वर के अंदर कोड होगी; वहां अधिक बड़े पैमाने पर "ओओ" कोड करने की कल्पना करना बहुत आसान है।)

+0

इसलिए जब मैं प्रोटोटाइप विधियों (नए कीवर्ड के माध्यम से) के साथ एक नई वस्तु को तुरंत चालू करता हूं, तो उस ऑब्जेक्ट को प्रत्येक फ़ंक्शन की एक नई प्रति नहीं मिलती है (केवल एक पॉइंटर)? यदि ऐसा है, तो आप प्रोटोटाइप का उपयोग क्यों नहीं करना चाहेंगे? – opl

+0

@marcel, d'oh ... =) – hellatan

+0

@ ओपी हाँ, आप सही हैं - कोई प्रतिलिपि नहीं बनाई गई है। इसके बजाए, प्रोटोटाइप ऑब्जेक्ट पर प्रतीकों (संपत्ति के नाम) प्रत्येक इंस्टेंस ऑब्जेक्ट के आभासी भागों के रूप में स्वाभाविक रूप से "वहां" होते हैं। एकमात्र कारण लोग इससे परेशान नहीं करना चाहते हैं, ऐसे मामले होंगे जहां वस्तुएं अल्पकालिक और विशिष्ट होंगी, या जहां साझा करने के लिए बहुत अधिक "व्यवहार" नहीं है। – Pointy

1

यदि मैं कक्षा आधारित अवधि में समझाता हूं तो व्यक्ति वर्ग है, चलना() प्रोटोटाइप विधि है । तो चलने() के साथ इसके नए ऑब्जेक्ट को तुरंत चालू करने के बाद ही इसका अस्तित्व होगा।

तो यदि आप ऑब्जेक्ट की प्रतियां बनाना चाहते हैं तो आप कई उपयोगकर्ताओं को बना सकते हैं प्रोटोटाइप अच्छा समाधान है क्योंकि यह स्मृति में प्रत्येक ऑब्जेक्ट के लिए फ़ंक्शन की एक ही प्रति साझा/विरासत में स्मृति को सहेजता है।

जबकि स्थिर इस तरह के परिदृश्य में उस महान सहायता नहीं है।

function Person(){ 
this.name = "anonymous"; 
} 

// its instance method and can access objects data data 
Person.prototype.walk = function(){ 
alert("person has started walking."); 
} 
// its like static method 
Person.ProcessPerson = function(Person p){ 
alert("Persons name is = " + p.name); 
} 

var userOne = new Person(); 
var userTwo = new Person(); 

//Call instance methods 
userOne.walk(); 

//Call static methods 
Person.ProcessPerson(userTwo); 

तो इसके साथ यह उदाहरण विधि की तरह है। ऑब्जेक्ट का दृष्टिकोण स्टेटिक विधियों की तरह है।

https://developer.mozilla.org/en/Introduction_to_Object-Oriented_JavaScript

40

यदि आप ऑब्जेक्ट की "गैर स्थैतिक" विधि घोषित करना चाहते हैं तो आपको प्रोटोटाइप का उपयोग करना चाहिए।

var myObject = function() { 

}; 

myObject.prototype.getA = function(){ 
    alert("A"); 
}; 

myObject.getB = function(){ 
    alert("B"); 
}; 

myObject.getB(); // This works fine 

myObject.getA(); // Error! 

var myPrototypeCopy = new myObject(); 
myPrototypeCopy.getA(); // This works, too. 
+4

+1 अच्छा बिंदु। ऐसा नहीं माना जाता था। – GFoley83

+0

@keatsKelleher लेकिन हम 'इस' उदाहरण 'this.getA = function() {alert ("ए")} का उपयोग कर कन्स्ट्रक्टर फ़ंक्शन के अंदर विधि को परिभाषित करके ऑब्जेक्ट के लिए एक गैर स्थैतिक विधि बना सकते हैं। –

13

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

कहें कि आपके पास बेस Car() कक्षा/वस्तु है।

function Car() { 
    // do some car stuff 
} 

तो आप एक से अधिक Car() उदाहरण बना।

var volvo = new Car(), 
    saab = new Car(); 

अब, आप जानते हैं कि प्रत्येक कार ड्राइव करने के लिए, पर बारी आदि के बजाय Car() वर्ग के लिए सीधे एक विधि संलग्न (जो बनाया प्रत्येक उदाहरण के प्रति स्मृति लगते हैं) की आवश्यकता होगी, तो आप करने के तरीकों संलग्न कर सकते हैं इसके बजाय प्रोटोटाइप (केवल एक बार विधियों का निर्माण), इसलिए नए volvo और saab दोनों के लिए उन विधियों तक पहुंच प्रदान करना।

// just mapping for less typing 
Car.fn = Car.prototype; 

Car.fn.drive = function() { 
    console.log("they see me rollin'"); 
}; 
Car.fn.honk = function() { 
    console.log("HONK!!!"); 
} 

volvo.honk(); 
// => HONK!!! 
saab.drive(); 
// => they see me rollin' 
+2

वास्तव में यह गलत है। volvo.honk() काम नहीं करेगा क्योंकि आपने प्रोटोटाइप ऑब्जेक्ट को पूरी तरह से बदल दिया है, इसे विस्तारित नहीं किया है। यदि आप ऐसा कुछ करना चाहते हैं तो यह आपके जैसा काम करेगा: Car.prototype.honk = function() {console.log ('HONK');} volvo.honk(); // 'हंक' – 29er

+1

@ 2 9र - जिस तरह से मैंने यह उदाहरण लिखा है, आप सही हैं। आदेश मायने रखता है। अगर मैं इस उदाहरण को इस तरह रखना चाहता था, तो 'कार.प्रोटोटाइप = {...}' को इस नई पहेली में दिखाए गए 'नई कार()' को कॉल करने से पहले आना होगा: http://jsfiddle.net/mxacA /। आपके तर्क के लिए, यह करने का सही तरीका होगा: http://jsfiddle.net/Embnp/। मजेदार बात यह है कि, मुझे इस प्रश्न का उत्तर याद नहीं है =) – hellatan

+0

@hellatan आप इसे कन्स्ट्रक्टर सेट करके ठीक कर सकते हैं: कार से जब आप किसी ऑब्जेक्ट के साथ प्रोटोटाइप प्रॉपर्टी को ओवरराइट करते हैं। –

संबंधित मुद्दे