जावास्क्रिप्ट: OOP, प्रोटोटाइप, क्लोजर, "क्लास" टाइमर ।js

हैलो प्रोग्रामर, शुरुआती, स्नातक, साथ ही साथ सभी सहानुभूति रखने वाले। जैसा कि आप जानते हैं, व्यक्तिगत अनुभव से कुछ भी ज्ञात नहीं है। मुख्य बात यह है कि अनुभव उपयोगी है। और इस सरल विचार की निरंतरता में, मैं एक ही बार में कई उपयोगी चीजें करने का प्रस्ताव करना चाहता हूं:

चेतावनी! अगर आपको लेख से कुछ भी मजेदार होने की उम्मीद नहीं है ... तो आप गलत हैं। हास्य की कम भावना वाले लोग पढ़ते हैं ... और भी अधिक अनुशंसित! खैर, चलिए शुरू करते हैं ...

मैं ध्यान देता हूं कि लेखन के समय (हैलो एपोकैलिक-ग्लैमरस 2012!) जावास्क्रिप्ट में कोई वास्तविक ओओपी नहीं है, लेकिन ऐसी चालें हैं जिनके द्वारा ऑब्जेक्ट-ओरिएंटेड प्रोग्रामिंग के बुनियादी सिद्धांतों को लागू किया जाता है। उन लोगों के लिए जो केवल इस पागलपन भरे रोचक विषय की खोज कर रहे हैं, मैं अपने शब्दों में इस विधि की विशिष्टता समझाऊंगा।

भाग 1. एक मानव चेहरे के साथ OOP।


संक्षेप में: OOP एक पवित्र मंत्र नहीं है, लेकिन अनिवार्य रूप से अनुप्रयोगों को व्यवस्थित करने, कोड को व्यवस्थित करने, विधियों को केंद्रीकृत करने और एकल पदानुक्रमित परिवार में संस्थाओं को संयोजित करने की एक तकनीक है। पनडुब्बी और विमान कैसे बनाए गए थे, इसी तरह वन्यजीवों से उछाल और अस्थिरता के अनुभव को अपनाते हुए, OOP एप्लिकेशन कुछ "जीवित" वस्तुओं के रूप में कार्यक्रम संस्थाओं की धारणा का उपयोग करते हैं, वास्तविक (ऑफ़लाइन - याद) दुनिया से हमारे लिए ज्ञात विशेषताओं और गुणों को अपनाते हैं? ।

दूसरे शब्दों में, एक इकाई बनाई जाती है, जिसमें न केवल अपने गुण और विधियां होती हैं, बल्कि वंशज उत्पन्न करने और विकसित करने में सक्षम होती है! इसे विस्तार कहा जाता है - विस्तार। एक सावधान माता-पिता की तरह, एक वस्तु विरासत द्वारा संपत्ति हस्तांतरित करती है, या पीढ़ियों का अनुभव प्राप्त करती है, एक अन्य मूल संस्था / माता-पिता का वंशज है। इस प्रकार, एक एकल पीढ़ी का पेड़ बनाया जाता है जिसमें यह नेविगेट करने के लिए सुविधाजनक है और विषम पुस्तकालयों के विपरीत, बड़े पैमाने पर प्रबंधन करने के लिए सुविधाजनक है - एक प्रक्रियात्मक विधि।

जैसा कि आप देख सकते हैं, सब कुछ वैसा ही है जैसा लोगों के पास है! इस अंतर के साथ कि डेवलपर इस प्रणाली का देवता है और इसे पीढ़ियों के माध्यम से पारित किया जा सकता है, जड़ में परिवर्तन या विकास की अलग-अलग शाखाओं में। हम युद्धों और संघर्षों को खत्म करते हैं! नया ज्ञान - जोड़! या इसके विपरीत, हम सब कुछ तोड़ देते हैं ... भगवान बनना कठिन है! हालाँकि, OOP सिद्धांत ही डेवलपर को नियमों के अनुसार आवेदन को ढाँढ़ने के लिए बाध्य करता है, बजाय अधीर करने के, जो इसके समर्थन को सुव्यवस्थित और व्यवस्थित करता है, और हालांकि, इस मामले में भी कोड को भ्रमित करने की इच्छा के साथ बिल्कुल भी हस्तक्षेप नहीं करता है ... :)

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

इस तरह के मजाकिया गंभीर रूप में, हमने इन दोनों को समझने के तरीके के रूप में, अमूर्त वर्गों और पारिवारिक संबंधों के विषय पर छुआ। लेकिन गंभीरता से, निश्चित रूप से, प्रोग्रामिंग में कोई यादृच्छिक तरीके नहीं होने चाहिए, और किसी भी तरीके और गुण का हिस्सा हैं। कक्षाओं का एक सुविचारित पदानुक्रम, जो एक परिवार के पेड़ की तरह, पीढ़ी से पीढ़ी तक कार्यक्षमता का विस्तार करने के अवसर प्रदान कर सकता है। और अमूर्त वर्ग, और यहां तक ​​कि अधिक सार वाले - इंटरफेस (इंटरफ़ेस में कोई भी कार्यान्वयन नहीं होता है) प्रोग्रामर को खोने में मदद करने के लिए नहीं, जीवन में सभी वंशजों के लिए आवश्यक सामान्य कौशल को लागू करने के लिए मत भूलना, जिसके बिना व्यक्ति मर जाएगा, और इसके साथ आवेदन।

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

विरासत शायद OOP की सबसे महत्वपूर्ण विशेषता है। यदि विकास के एक नए दौर की आवश्यकता होती है, तो प्रोग्रामर एक नया वर्ग बनाता है जो अपने माता-पिता के कौशल का विस्तार करता है, और कभी-कभी एक नए तरीके से लागू होता है, अर्थात। ओवरलैपिंग माता-पिता के तरीके - ओवरराइड। आखिरकार, जीवन में प्रत्येक पीढ़ी की अपनी अवधारणाएं होती हैं ... यदि किसी प्रोग्रामर को पिछली पीढ़ियों की "अनुभव और अवधारणाओं" की आवश्यकता होती है, तो वह उनकी ओर मुड़ता है। इस संरचना में कुछ भी नहीं खोया है, इसलिए इसका उपयोग करने में सक्षम होना बेहद जरूरी है।

और जबकि जावास्क्रिप्ट के लिए अभी तक कोई पूर्ण ओओपी विनिर्देश नहीं है, ओओपी के सिद्धांतों का पालन करने का एक अवसर है, और हम अब इस सुविधा का उपयोग करेंगे। बेशक, इस लेख के ढांचे के भीतर, हम केवल समझ की मूल बातें पर स्पर्श करेंगे, लेकिन जैसा कि आप जानते हैं - परेशान करने वाली परेशानी शुरू हुई, मुख्य बात यह है कि ...

इसलिए, हमारा लक्ष्य अब किसी प्रकार की नियंत्रित इकाई को लिखना है, जो कि टाइमर द्वारा, हमारे द्वारा आवश्यक प्रक्रियाओं को शुरू करेगा। "प्रबंधित" - इसका अर्थ है ऐसी इकाई, या सशर्त रूप से एक वर्ग, में समाहित होगा - जैसा कि वे कहते हैं कि संक्षिप्त करना, नियंत्रण के लिए तरीके और गुण जिनमें आवश्यक डेटा शामिल हैं। जीवन उदाहरण:
• गुण - यह वह है जो वस्तु जानता है (नाम, आंखों का रंग, गुणन तालिका),
• विधियाँ - यह वह है जो एक वस्तु कर सकती है (सो, खा, एक सिनक्रोपसोट्रॉन का निर्माण)।

महत्वपूर्ण! यदि पाठक को अभी तक यह पता नहीं है कि जावास्क्रिप्ट में ऑब्जेक्ट क्या है, तो मैं सुझाव देता हूं कि आप इसके बारे में पहले से किसी संदर्भ में पढ़ें, अन्यथा समझने में मुश्किलें आएंगी।
एक ऑब्जेक्ट बनाना एक फ़ंक्शन के माध्यम से होगा जिसे नए निर्देश के साथ कहा जाता है। यह नया निर्देश है जो निर्धारित करता है कि यह फ़ंक्शन बिल्कुल भी सामान्य नहीं है, लेकिन एक विशेष फ़ंक्शन - कंस्ट्रक्टर, जो एक निश्चित वस्तु बनाता है और वापस करता है। इससे पहले कि वह इसे वापस करे, हम इस वस्तु को वह सब कुछ सौंप सकते हैं जो आत्मा की इच्छा है: ज्ञान और कौशल दोनों।

function Timer() { /*    … */ }; var timer = new Timer(); 


इसलिए, हमने कक्षा टाइमर का एक टाइमर ऑब्जेक्ट बनाया। लेकिन रचनात्मक लोगों के रूप में, हम पहले से ही किसी वस्तु को बनाते समय, या उसे बंद न करते हुए कुछ गुणों के साथ अपनी वस्तु को समाप्त करना चाहते हैं हम सार्वभौमिकता चाहते हैं ताकि, यदि वांछित हो, तो हम "ज्ञान और कौशल" पूछ सकते हैं या नहीं, लेकिन वस्तु सांस लेने में सक्षम होने के बिना पीड़ा में मर नहीं जाएगी, उदाहरण के लिए ... हम जानवर नहीं हैं, लेकिन यह कैसे करें?
ऐसा करने के लिए, पहली चीज जिसे हम अपनी कक्षा में रखते हैं, वह डिफ़ॉल्ट गुण है जिसे वस्तु प्रकृति द्वारा स्वीकार करेगी। और प्रसंस्करण इकाई।

 function Timer( options ) { //public var defaultOptions = { delay: 20 //     ,   -   ,stopFrame: 0 //   ,loop: true //    ,frameElementPrefixId: 'timer_' //   ID ,  -  } for(var option in defaultOptions) this[option] = options && options[option]!==undefined ? options[option] : defaultOptions[option]; /*   … */ }; 


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

 var timer = new Timer( {delay: 500, loop: false } ); 


... और निर्दिष्ट गुण लिखे जाएंगे, और छूटे हुए चूक डिफ़ॉल्ट से लिए जाएंगे। बचाई गई वस्तु! बहुमुखी प्रतिभा और लचीलापन प्राप्त हुआ!

टिप्पणियों पर ध्यान दें //public
बेशक, इसका एक सशर्त मूल्य है (जैसे जावास्क्रिप्ट में सभी ओओपी सम्मेलनों, एक वर्ग की अवधारणा सहित), लेकिन इसका सार सार्वजनिक गुणों के निशान में है, अर्थात बाहर से उपलब्ध है। इन गुणों को ऑब्जेक्ट के माध्यम से सीधे एक्सेस किया जा सकता है:
 alert( timer.delay ); 


जीवन से एक उदाहरण के रूप में, ये उस वस्तु के स्पष्ट गुण हैं जिन्हें छिपाने की आवश्यकता नहीं है: बिल्ली की पूंछ की लंबाई।

व्यक्तिगत गुण भी हैं - निजी, और संरक्षित - संरक्षित। व्यक्तिगत तक पहुंचने के लिए, यदि यह बिल्कुल स्वीकार्य है, तो आपको विशेष विधियों का उपयोग करने की आवश्यकता है जिसमें प्रोग्रामर निर्धारित करता है कि क्या और कैसे और किससे वापस आ सकते हैं। संरक्षित - संरक्षित, यह थोड़ा कम व्यक्तिगत है, क्योंकि "परिवार" के घेरे में, वर्ग और उसके उत्तराधिकारियों दोनों के लिए सुलभ है, इसलिए बोलने के लिए। यह आवेदन की स्थिरता के लिए किया जाता है, क्योंकि सभी बकवास अपनी झोपड़ियों को सहन करने के लिए बेहतर नहीं हैं ...

आइए और हम व्यक्तिगत गुण जोड़ते हैं - निजी, यह फ़ंक्शन के अंदर आंतरिक चर बनाकर किया जाता है, इसलिए फ़ंक्शन द्वारा उनका दायरा सीमित (या अन्यथा बंद) है - "यदि मैं नहीं चाहता तो मैं किसी को नहीं बताऊंगा!" हम क्लोजर के बारे में बात करेंगे ... इस बीच, हम टाइमर फ़ंक्शन में आगे सम्मिलित करते हैं:

  //private var busy = false, //  ""  " !" currentTime = 0 //   ,frame = 1 //   ,task = {} // ! !         ,   ,keyFrames=[] //    , ..      ; /*   … */ 

ऐसी संपत्तियों के लिए, यदि हम चाहें, तो हम सार्वजनिक विधियों के माध्यम से पहुंच की अनुमति देंगे:
 this.getKeyFrames = function( ) { return keyFrames; } 

ध्यान दें कि यह विधि सार्वजनिक है, क्योंकि इसके माध्यम से, इसे ऑब्जेक्ट की संपत्ति को सौंपा गया है, जिसे तब बिंदु के माध्यम से एक्सेस किया जा सकता है (यदि हम कार्रवाई को कॉल करते हैं तो अंत में ब्रैकेट्स के बारे में मत भूलना):

 timer.getKeyFrames(); 


अगर हमें एक निजी विधि की आवश्यकता है, तो यह निजी चर की तरह, एक आंतरिक फ़ंक्शन की "सामान्य" घोषणा द्वारा भी बनाई जाती है:

 function somePrivateMethod() { /* some code... */ } 


यह एक सेवा फ़ंक्शन निकला जिसे डिज़ाइनर व्यक्तिगत उद्देश्यों के लिए कॉल कर सकता है। लेकिन कंस्ट्रक्टर द्वारा बनाई गई ऑब्जेक्ट से, यह फ़ंक्शन उपलब्ध नहीं होगा, क्योंकि उसकी संपत्ति नहीं।

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

ठीक है, की तरह, हमने थोड़ी पहुंच के संगठन को छांटा। विरासत के बारे में क्या, क्योंकि यह ओओपी में सबसे महत्वपूर्ण गुण है - अपने माता-पिता के कौशल और ज्ञान को अपनाने और विकसित करने की क्षमता? और यहाँ हम जावास्क्रिप्ट की एक महत्वपूर्ण विशेषता पर आते हैं - प्रोटोटाइप विरासत। यह विषय अक्सर समझने में कठिनाइयों का कारण बनता है, और फिर मैं एक सरल, मानवीय भाषा में "सब कुछ एक बार और सभी के लिए" समझाने का प्रयास करूंगा।

भाग 2. जावास्क्रिप्ट में प्रोटोटाइप।


तो, जावास्क्रिप्ट में एक वस्तु की एक प्रोटोटाइप, एक छिपे हुए लिंक [[प्रोटोटाइप]] की अवधारणा है, यह __proto__ भी है, और फ़ंक्शन का प्रोटोटाइप गुण भी है। इन अवधारणाओं में भ्रमित होने से रोकने के लिए, हम एक बार में उनका विश्लेषण करेंगे:



किसी भी स्वाभिमानी जावास्क्रिप्ट ऑब्जेक्ट में एक छिपा हुआ लिंक होता है [[प्रोटोटाइप]], जो इसे मूल वस्तु के साथ जोड़ता है, जो बदले में अपने स्वयं के साथ होता है, आदि। इस श्रृंखला के शीर्ष पर एक अंतर्निहित जावास्क्रिप्ट ऑब्जेक्ट है, एक प्रकार का सर्वोच्च पूर्वज, एक वस्तु एडाम जिसमें सभी आवश्यक अंतर्निहित तरीके होते हैं, जैसे कि स्ट्रींग, वैल्यूऑफ, हैवऑनप्रोपरेटी, आदि। इसके कारण, सभी वंशज वस्तुओं में तरीकों का यह न्यूनतम आवश्यक सेट है जो उन्हें एक कठिन जावास्क्रिप्ट वातावरण में जीवित रहने की अनुमति देता है।

thread_object2 - [[प्रोटोटाइप]] -> thread_object1 - [[प्रोटोटाइप]] -> ... {toString: ..., valueOf: ..., hasOwnProperty: ..., ...}

यानी यहां तक ​​कि अगर आप बस एक खाली var obj = {} ऑब्जेक्ट बनाते हैं जिसमें विधियाँ और गुण नहीं हैं, और मानक विधि की ओर मुड़ते हैं, तो [[प्रोटोटाइप]] लिंक चेन (इस मामले में, एक न्यूनतम शॉर्ट चेन) का उपयोग करते हुए, यह अंतर्निहित जावास्क्रिप्ट ऑब्जेक्ट से इस विधि को ले जाएगा:

 var obj = {}; //  obj.toString(); //      "[object Object]" 


इस प्रकार प्रोटोटाइप विरासत को जावास्क्रिप्ट में कार्यान्वित किया जाता है, लिंक [[प्रोटोटाइप]] की एक श्रृंखला के माध्यम से। प्रोटोटाइप श्रृंखला के माध्यम से उपलब्ध सभी संपत्तियां ज्ञान और कौशल के भंडार की तरह, वंशजों के लिए खुली होंगी, जो आपको कक्षाओं के एक वास्तविक विकसित पेड़ बनाने की अनुमति देता है!

ध्यान दें कि प्रत्येक वस्तु के प्रोटोटाइप गुण उसमें संग्रहीत नहीं हैं, लेकिन वर्तमान श्रृंखला और अंतर्निहित जावास्क्रिप्ट ऑब्जेक्ट के बीच "समय की शुरुआत में" प्रोटोटाइप श्रृंखला के मध्यवर्ती लिंक में हैं। इस रूट ऑब्जेक्ट को सम्मान की आवश्यकता है, और इसकी शांति को परेशान करना काफी सभ्य नहीं है, इसलिए, अपने स्वयं के प्रोटोटाइप गुणों को बनाने के लिए, लिंक [[प्रोटोटाइप]] के साथ श्रृंखला में अपनी खुद की वस्तुओं को बनाना और एम्बेड करना बेहतर है।

लेकिन, जैसा कि हम याद करते हैं, [[प्रोटोटाइप]] एक बंद कड़ी है, हम अपनी पहुंच के बिना अपनी श्रृंखला का निर्माण कैसे कर सकते हैं? यहां हमें नए कीवर्ड और प्रोटोटाइप प्रॉपर्टी के साथ पहले से ही परिचित कंस्ट्रक्टर फ़ंक्शन द्वारा मदद की जाती है, जो अपने आप में काफी खुली हुई है। तथ्य यह है कि कंस्ट्रक्टर के माध्यम से बनाई गई वस्तु को इस कंस्ट्रक्टर की प्रोटोटाइप संपत्ति में निर्दिष्ट मूल्य के साथ [[प्रोटोटाइप]] लिंक मिलता है! प्रारंभ में, किसी भी फ़ंक्शन की प्रोटोटाइप संपत्ति में लगभग खाली वस्तु (एकल निर्माता संपत्ति के साथ फ़ंक्शन को वापस इंगित करने के लिए) का संदर्भ होता है,

function_protection.prototyp -> {constructor: -> function_profection}

लेकिन हम अपने मूल वर्ग में उत्तीर्ण होकर प्रोटोटाइप संपत्ति को बदल सकते हैं। यानी कंस्ट्रक्टर फ़ंक्शन का निर्माण करते समय, हम केवल उस ऑब्जेक्ट के गुणों के संदर्भ में संदर्भ देते हैं, जिसकी हमें उसकी प्रोटोटाइप प्रॉपर्टी में आवश्यकता होती है, और हमारे द्वारा बनाई गई नई ऑब्जेक्ट को इस ऑब्जेक्ट के साथ एक [[प्रोटोटाइप]] लिंक मिलता है, जिसकी हमें ज़रूरत होती है।

 //   "" var Foo = function() {}; //  prototype      Foo.prototype = { hi: 'Hello!', sayHi: function(){ alert( this.hi ) } }; //   "" var obj = new Foo(); //  ,    obj.sayHi(); 


ऊपर दिए गए उदाहरण में, ओब्जेक्ट ऑब्जेक्ट के छिपे हुए लिंक [[प्रोटोटाइप]] को हाई प्रॉपर्टी और SayHi विधि के साथ ऑब्जेक्ट के लिए एक पॉइंटर मिला। इस प्रकार, ओब्जेक्ट ऑब्जेक्ट को यह ज्ञान और कौशल विरासत में मिला है।
इस प्रक्रिया को सरल बनाने के लिए, एक फ़ंक्शन का आविष्कार किया गया था।

 function extend(Child, Parent) { var F = function() { } F.prototype = Parent.prototype Child.prototype = new F() Child.prototype.constructor = Child Child.superclass = Parent.prototype } 


इसके उपयोग का एक उदाहरण, javascript.ru देखें
javascript.ru/tutorial/object/inheritance#svoystvo-prototype-i-prototip

यह दो कार्य करता है - कंस्ट्रक्टर - अवरोही और जनक - तर्कों में, और वह करता है जो हमने पहले ही उल्लेख किया है:


सवाल यह उठ सकता है कि हमें पहली तीन पंक्तियों की आवश्यकता क्यों है, क्यों नहीं तुरंत असाइनमेंट बनाइए Child.prototype = Parent.prototype, बिना किसी नए F () के, और अंत है?

तथ्य यह है कि इस असाइनमेंट के साथ विरासत श्रृंखला में एक नया मध्यवर्ती लिंक नहीं बनाया जाएगा! Parent.prototype को Child.prototype को लिखा गया है, और Parent.prototype के आगे संदर्भ के साथ इंटरमीडिएट स्टोरेज ऑब्जेक्ट को नहीं, और जब हम Child.prototype को कुछ भी लिखने का प्रयास करते हैं, तो हम बड़ों के प्रति सम्मान और पीढ़ियों के उत्तराधिकार का उल्लंघन करते हुए, मूल रूप से पेरेंट क्षेत्र में टूट जाएंगे। नए F () कंस्ट्रक्टर को कॉल करके, हम प्रोटॉपिकल नॉलेज को स्टोर करने के लिए चाइल्ड के लिए अपना खुद का क्षेत्र बनाते हैं जिसे वह अपने वंशजों को दे सकता है।

आप निम्नलिखित तरीके से डिजाइनर के प्रोटोटाइप में अलग-अलग गुण जोड़ सकते हैं:

 Child.prototype.someProperty = "someProperty"; 


और वैसे, आपको किसी ऑब्जेक्ट की संपत्ति के रूप में प्रोटोटाइप को संदर्भित करने का प्रयास नहीं करना चाहिए - एक वर्ग का एक उदाहरण।
ऑब्जेक्ट में एक प्रोटोटाइप संपत्ति नहीं है, इसमें एक छिपा हुआ लिंक [[प्रोटोटाइप]] है, लेकिन प्रोटोटाइप संपत्ति नहीं है!
यह बेशक बनाया जा सकता है, लेकिन इससे विरासत में कोई मतलब नहीं है। सेंस केवल कंस्ट्रक्टर फ़ंक्शन के प्रोटोटाइप संपत्ति से है, जो कि निर्मित वस्तु के [[प्रोटोटाइप]] लिंक के लिए एक सूचक को पास करने की क्षमता के कारण है।

यह प्रोटोटाइप वंशानुक्रम के लिए है। क्या यह वास्तव में सरल है?
लेकिन प्रोटोटाइप उत्तराधिकार केवल संभव योजना नहीं है। मैं सुपरक्लास के निर्माता को कॉल करने की विधि का भी उल्लेख करना चाहता हूं, अर्थात्। मूल वर्ग, यह कुछ भी नहीं है कि हमने इसे प्रोटोटाइप गुणों के लिए लिखने का ध्यान रखा है (फ़ंक्शन का विस्तार देखें)।

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

 function TimerPlayer() { TimerPlayer.superclass.constructor.apply( this, arguments ); } 


यहां यह याद रखना महत्वपूर्ण है कि आप इस के माध्यम से कॉल नहीं कर सकते। superclass.constructor.apply, अर्थात वर्तमान कंस्ट्रक्टर (यहाँ TimerPlayer) के नाम के माध्यम से, क्योंकि अन्यथा, यदि मूल निर्माणकर्ता भी इसका उपयोग करता है, और कॉल करता है। superclass.constructor.apply (यह) तर्क), यह एक वंशज के रूप में इसे लागू करने के लिए एक बंद कॉल में बदल जाएगा, जो एक त्रुटि का कारण होगा।

वंश के संदर्भ में मूल निर्माणकर्ता को एक कॉल, वंशज को उसके सभी गुणों और विधियों को बनाएगा और असाइन करेगा। इसके अलावा, माता-पिता के निजी गुण, जिन्हें var के माध्यम से घोषित किया गया है, और इसके माध्यम से नहीं, केवल तभी पहुँचा जा सकता है जब माता-पिता सार्वजनिक तरीके हैं जो उन्हें पढ़ने की अनुमति देते हैं।
यह इस तरह से है कि हम अपने टाइमर का निर्माण जारी रखें।

भाग 3. जावास्क्रिप्ट क्लास टाइमर और इसकी विरासत।


इसलिए, हमारे पास पहले से ही एक वर्ग है जो कुछ जानता है, लेकिन यह नहीं जानता कि कैसे, इसलिए इसमें कौशल जोड़ने का समय है! हम अपनी कक्षा को क्या सिखाना चाहते हैं? चलो एक खिलाड़ी की तरह कुछ बनाते हैं:
• शुरू करो
• ठहराव
• बंद करो
• उल्टा
• सेटटॉफ्रेम
और कुछ कम महत्वपूर्ण तरीके। कल्पना कीजिए कि हमने उन्हें पहले ही लिखा था ... इसलिए, हम टाइमर समारोह में आगे सम्मिलित करते हैं:

 this.start = function(){ /*  */ if( busy ) return; if( window.console ) console.log ('start: .currentTime='+currentTime+'; frame='+frame); busy = true; timer.call( this ); } this.pause = function() { /*  */ if( window.console ) console.log ('pause: currentTime='+currentTime+'; frame='+frame); clearInterval( this.intervalId ); busy = false; } this.stop = function() { /*  */ if( window.console ) console.log ('stop: currentTime='+currentTime+'; frame='+frame); clearInterval( this.intervalId ); busy = false; currentTime = 0; frame = 1; this.clearFrameLine(); } /* highlighting -   */ this.clearFrameLine = function() { /*    */ for(var i=1, str=''; i<this.stopFrame+1; i++) if( elFr = document.getElementById( this.frameElementPrefixId+i ) ) removeClass( elFr, 'active'); } this.setActiveFrameElement = function( frameNumber ){ /*    */ if( elFr = document.getElementById( this.frameElementPrefixId+frameNumber ) ) addClass(elFr, 'active'); } this.toString = function() { /*  ,   alert(),    */ var str = ''; for(var option in this ) str+= option+': '+( (typeof this[option]=='function') ? 'function' : this[option] )+'\n'; return '{\n'+str+'}'; } this.setTask = function( new_task ) { /*   ,       */ task = new_task; this.stopFrame = 0; keyFrames.length = 0; for(var frInd in task) { if( (+this.stopFrame)< (+frInd) ) this.stopFrame=(+frInd); keyFrames.push( +frInd ); } } this.getKeyFrames = function( ) { /*    keyFrames */ return keyFrames; } this.getTask = function() { /*    task */ return task; } this.setToFrame = function( toFrame ) { /*     */ if(toFrame>this.stopFrame) return; frame=toFrame; currentTime=(frame-1)*this.delay; for(var frInd in task) { if( (+frInd)>(+toFrame) ) break; var taskList = task[ frInd ]; for(var i=0; i<taskList.length; i++ ){ var taskItem; if( taskItem = taskList[i] )taskItem.run(); } } this.clearFrameLine(); this.setActiveFrameElement( toFrame ); } this.rewind = function( amount ) { /* !  !      :))))))))) */ if( amount<0 && this.intervalId ) amount--;/*    setInterval */ var toFrame = frame+amount; toFrame = Math.max( Math.min( toFrame, this.stopFrame), 1); this.setToFrame(toFrame); } function timer(){ /*  ,  setInterval      */ var this_ = this; /*         */ this.intervalId = setInterval( function() { /*    setInterval    this.delay */ //console.log ('currentTime='+currentTime+'; frame='+frame+';'+task); if( task[ frame ] ) { /*       ,  ... */ var taskList = task[ frame ] /* ...   -   */ for(var i=0; i<taskList.length; i++ ){ /*     -    - run... */ var taskItem; if( taskItem = taskList[i] ) taskItem.run(); /* ...     */ } } /* highlighting */ this_.setActiveFrameElement( frame ); /*   */ currentTime+=this_.delay; /*      */ frame++; if( this_.stopFrame && frame>this_.stopFrame ) { /*  stopFrame       ... */ if( this_.loop ) this_.setToFrame( 1 ); /*    - ,    ,   ,  , */ else this_.stop(); /*   ! */ } }, this.delay ); } 


सभी डिजाइनर के साथ समाप्त हो गया।
सामान्य तौर पर, मुझे लगता है कि सब कुछ स्पष्ट है: क्या आपको एक शुरुआत विधि की आवश्यकता है? हम एक सार्वजनिक शुरुआत विधि लिख रहे हैं! कहाँ ...

 this.start = function(){ if( busy ) return; /*    ,  ! */ /*   ,     */ if( window.console ) console.log ('start: .currentTime='+currentTime+'; frame='+frame); busy = true; /*  ,   */ timer.call( this ); /*    */ } 


मैंने टाइमर फ़ंक्शन पर स्वयं विस्तार से टिप्पणी की। पूरा विचार सरल है:

 function timer(){ var this_ = this; this.intervalId = setInterval(function() { /*    ,  this_   this! */ }, this.delay ); } 


सबसे पहले, हम अपनी वस्तु के संदर्भ के लिंक को एक चर में सहेजते हैं, क्योंकि सेट इन्टरवल में फ़ंक्शन के अंदर, संदर्भ खो जाएगा, और चर क्लोजर में रहेगा, अर्थात स्थानीय दायरे में। शायद समझने के लिए, हमें क्लोजर के बारे में दोहराना (या पता लगाना) चाहिए, और हम बाद में उनके बारे में बात करेंगे ... अगला, हम अपनी वस्तु को इंटरवल आइडी असाइन करते हैं, जिसे सेट्टरवल विधि द्वारा लौटाया जाता है, यह पहचानकर्ता हमें रोक दिया जाता है जब रोका या रोका जाता है, तो इन तरीकों को देखें।

कार्य संपत्ति को एक अलग विश्लेषण की आवश्यकता है, क्योंकि यह वहां है कि हम निष्पादन के लिए कुछ फॉर्म स्टोर कार्यों में हैं। इसकी संरचना इस प्रकार है:

 { 1:[ { run: function(){} } ], 5:[ {},{},{} /*...*/ ], /*...*/ } 


ऑब्जेक्ट ऑब्जेक्ट की एक सरणी है। ओह, मैंने इसे बेहतर नहीं कहा, अन्यथा मैं भ्रमित हो गया ...
लेकिन सब कुछ सरल है, वांछित फ्रेम संख्या के तहत टास्क ऑब्जेक्ट में रन संपत्ति के साथ कार्य ऑब्जेक्ट्स की एक सरणी होती है। इस संपत्ति को एक फ़ंक्शन सौंपा जाना चाहिए, जिसे वांछित फ्रेम कहा जाता है। यदि आवश्यक हो, प्रत्येक कार्य-वस्तु, आप एक और संपत्ति जोड़ सकते हैं, तो वह और वस्तु।

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

इस प्रकार, कार्य को भरना और सेटटस्क विधि के साथ हमारी कक्षा को असाइन करना, हम यह निर्धारित करते हैं कि क्या करना है और कब करना है। इसका उपयोग कैसे किया जा सकता है? साइट पर या ऑफ़लाइन क्लाइंट पर विभिन्न गतिशील परिदृश्यों का प्रदर्शन करें, एनिमेशन बनाएं, समय के लिए बंधे "लाइव" ट्यूटोरियल या परीक्षण बनाएं, आपको महत्वपूर्ण घटनाओं की याद दिलाएं (केतली उबल रही है!), और उपयोगकर्ताओं को पॉप-अप विज्ञापन (एक गंदा और गंदा मजाक) प्राप्त करें। या सिर्फ पृष्ठ के कोने में घड़ी प्रदर्शित करें!

इसके अलावा, हमने पहले से ही अपने टाइमर और विज़ुअलाइज़ेशन को नियंत्रित करने के लिए सबसे सरल इंटरफ़ेस, एक प्रकार का छोटा एपीआई आयोजित किया है और अब हम इसका उपयोग करते हैं, एक खिलाड़ी की तरह एक नियंत्रण कक्ष का निर्माण करते हैं! Html पेज पर पसंदीदा और प्रिय कोड डालें:

 <button onclick="timer.rewind(-50);">rewind -50</button> <button onclick="timer.start();">start</button> <button onclick="timer.pause();">pause</button> <button onclick="timer.stop();">stop</button> <button onclick="timer.rewind(+50);">rewind +50</button> 


ऑनक्लिक ईवेंट पर, टाइमर ऑब्जेक्ट के तरीकों पर बटन लटकाए जाते हैं। बेशक, कॉल करने से पहले आपको किस ऑब्जेक्ट को बनाने के लिए नहीं भूलना चाहिए। याद है कैसे? - कंस्ट्रक्टर फ़ंक्शन के माध्यम से:
 var timer = new Timer(); 


हालाँकि, अब स्क्रिप्ट बनाना बुरा नहीं होगा, इसे कैसे प्रबंधित किया जाए, अन्यथा, आप किस लिए लड़ रहे थे? ..
आइए एक सरल एनीमेशन बनाने का प्रयास करें, हम चित्र को पृष्ठ के चारों ओर स्थानांतरित करेंगे, ठीक है, एनीमेशन की दुनिया में "हैलो वर्ल्ड" का एक प्रकार। हम तस्वीर को आगे बढ़ाएंगे
 <img id="ball" src="http://www.smayli.ru/data/smiles/transporta-854.gif" > 



मैं आपको याद दिलाता हूं कि हमारे टाइमर का कार्य एक कार्रवाई को ट्रिगर करना है, जो भी कार्रवाई हमें पसंद है, जबकि वह खुद इस कार्रवाई के लिए जिम्मेदार नहीं है। इसलिए, वास्तव में क्या करना हमारा काम है, और हम अब एक तत्व को स्थानांतरित करने के लिए एक सरल फ़ंक्शन लिखकर इसे हल करेंगे, जो कि तत्व को आईडी और उसके दो निर्देशांक द्वारा स्वयं स्थानांतरित करता है:

 function moveElem( elem, top, left ){ elem.style.cssText = 'position:relative;top:'+top+'px;left:'+left+'px'; } 


तो, अब इस फ़ंक्शन को टास्क ऑब्जेक्ट की आवश्यक संपत्ति की संख्या के तहत सरणियों में टास्क ऑब्जेक्ट की रन संपत्ति को सौंपा जाना चाहिए, विचार के लिए बाहर देखें? इसलिए, हम एक स्क्रिप्ट ऑब्जेक्ट बनाते हैं, और इसके पहले फ्रेम को एक सरणी के रूप में परिभाषित किया जाता है, इस फ्रेम में हम चित्र तत्व की प्रारंभिक स्थिति रखते हैं:

 var frames = {}; /*   */ frames[1]=[]; frames[1].push( { run: function(){ moveElem( ball, 600, 600 ); } } ); 


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

और वोइला!पहले फ्रेम में, हमने एक एक्शन जोड़ा, जो हमारी तस्वीर (गुब्बारे) को पृष्ठ पर एक निश्चित निचले स्थान पर रखता है। अब, उठना शुरू करने के लिए, हमें इस राज्य फ्रेम को फ्रेम से बदलना होगा, अर्थात। समन्वय को कम करें, अच्छी तरह से, और बाएं - हवा के लिए समायोजित। :) आवश्यक फ्रेम भरने के लिए, लूप का उपयोग करें। यदि आप चाहें, तो आप टाइमर क्लास की अपनी विधि लिख सकते हैं - जो फ्रेम जोड़ देगा, और क्रियाएं, और फ़्रेम द्वारा क्रियाओं के बदलते मापदंडों को वितरित कर सकता है ... इस बीच, उदाहरण के लिए, हम 2 से 600 मीटर तक लूप के साथ फ्रेम भरेंगे।

 /*  */ for(var i=2; i<601; i++) frames[i] = [ { run: function(i){ return function(){ moveElem( ball, 600-i, 600-i ); } }(i) } ]; 

यहां आपको क्लोजर के उपयोग पर भी ध्यान देना चाहिए। तथ्य यह है कि गतिशील i को स्थानांतरित करने के लिए, यहां हम एक कार्यात्मक अभिव्यक्ति में एक रैपिंग का उपयोग करते हैं जिसे जगह में कहा जाता है:

 function(i){ /*    i    */ }(i) ; 


अगर हम सिर्फ इस्तेमाल करते हैं:

 run: function(){ moveElem( ball, 600-i, 600-i ); } 


फिर, जब कॉल मूवमेंट () मैं वैश्विक दायरे से लिया जाएगा, अर्थात्। वह जिसे हमने घोषित किया है और लूप के लिए काम किया है (var i = 2; i <601; i ++), i.e. 600 के बराबर "वर्क आउट" वेरिएबल कहा जाएगा, लेकिन डायनेमिक मैं नहीं, जिसे धीरे-धीरे बढ़ाना चाहिए, टेक-ऑफ बॉल के निर्देशांक को बदलना।
इसलिए, हम जावास्क्रिप्ट में CLOSE का उपयोग करते हैं, जो गुंजाइश को परिभाषित करता है, अर्थात। एक समारोह में प्रदर्शन करते हुए

 function(i){ /*    i    */ }(i) ; 


... इसके अंदर, इसके स्वयं के चर को इस फ़ंक्शन के एक तर्क के रूप में बनाया गया था, और जगह में एक कॉल (दोहराएं यदि आप भूल गए), फ़ंक्शन के शरीर को निष्पादित करता है, जहां हमारा फ़ंक्शन लौटता है। और फिर से:
नहीं

 return moveElem( ball, 600-i, 600-i ); 


और

 return function(){ moveElem( ball, 600-i, 600-i ); } 


क्योंकि, पहले मामले में, फ़ंक्शन कॉल वापस नहीं किया जाएगा, लेकिन मूवमेंट (बॉल, 600-आई, 600-आई) पर कॉल का परिणाम, जिसे वहीं निष्पादित किया जाएगा!

और अब हमारे पास एक स्क्रिप्ट है। अब आप इसे असाइन कर सकते हैं और इसे चला सकते हैं:

 var timer = new Timer( ); timer.setTask( frames ); timer.start(); /*   start */ 


सामान्य तौर पर, फ़्रेम-बाय-फ़्रेम नियंत्रण दिलचस्प और जटिल परिदृश्य बनाने के लिए व्यापक संभावनाएं प्रदान करता है। Denis-or-love.narod.ru/portf/timer
डेमो पेज पर , मैंने Adobe Flash :) - drawFrameLine बटन में टाइमलाइन जैसी टाइम-लैप लाइन का एक उदाहरण लागू किया है।

यह ध्यान दिया जाना चाहिए कि पृष्ठ पर बड़ी संख्या में तत्व अपनी स्थिति को फिर से परिभाषित करते समय ब्राउज़र को धीमा कर सकते हैं, यह प्यारे IE में स्पष्ट रूप से दिखाई देता है, यदि आप पैरामीटर 1 - प्रत्येक फ्रेम के साथ drawFrameLine पर क्लिक करते हैं।
यदि वांछित है, तो आप एक्स्टेंसिबल सुविधाओं और अन्य सुविधाओं के साथ स्क्रिप्ट बनाने के लिए एक संपूर्ण इंटरफ़ेस लिख सकते हैं। यहाँ, जैसा कि वे कहते हैं, जो

छवि

इस तरह के एक बड़े सौदे में है ... और अब, चलो मिलें !
आधार टाइमर वर्ग का निर्माण करने के बाद, हम अधिक उन्नत टाइमरप्लेयर श्रेणी बनाकर इसका विस्तार करेंगे। आइए हम उन्नति के उदाहरण को एक सरल उदाहरण तक सीमित करें - हमारा बच्चा वर्ग, माता-पिता के कौशल को लेते हुए, एक खिलाड़ी की तरह हमारे टाइमर के साथ एक नियंत्रण कक्ष बनाने में सक्षम होगा। ऐसा करने के लिए, हम तीन काम करते हैं:
  1. पैरेंट कंस्ट्रक्टर को बुलाओ
  2. नए तरीके जोड़ना
  3. विस्तार फ़ंक्शन के माध्यम से वंशानुक्रम गुजर रहा है


 //  function TimerPlayer( options ) { //    TimerPlayer.superclass.constructor.apply(this, arguments); //   this.drawPanel = function( panelId, objName ) { var objName = objName || 'timer'; var template ='<button onclick="'+objName+'.rewind(-50);">rewind -50</button>'+ '<button onclick="'+objName+'.start();">start</button>'+ '<button onclick="'+objName+'.pause();">pause</button>'+ '<button onclick="'+objName+'.stop();">stop</button>'+ '<button onclick="'+objName+'.rewind(+50);">rewind +50</button>'; document.getElementById( panelId ).innerHTML = template; } } // extend extend(TimerPlayer, Timer); 


हालाँकि, हमारे यहाँ प्रोटोटाइप इनहेरिटेंस नहीं है, हमें भविष्य के लिए श्रृंखला बनाने के लिए विस्तार करना होगा (यदि हम माता-पिता के लिए प्रोटोटाइप गुण जोड़ना चाहते हैं ...), और सुपरक्लास और कंस्ट्रक्टर लिखने के लिए। नई ड्रापैनेल विधि उस तत्व के आईडी स्ट्रिंग को स्वीकार करती है जिसके भीतर बटन लगाए जाते हैं, और स्ट्रिंग HTML टेम्पलेट में प्रतिस्थापित की जाने वाली वस्तु का नाम है।

 //   var timerPlayer = new TimerPlayer(); timerPlayer.setTask( frames2 ); timerPlayer.drawPanel( 'controlPanel', 'timerPlayer' ); 


इसलिए हम समाप्त हो गए, या शायद हमारे टाइमर वर्ग और इसके वंश को शुरू किया।
मैं चाहता हूँ करने के लिए धन्यवाद समर्पित इस लेख लिखने में रोगी परामर्श और सहायता के लिए।
जब तक हम फिर से मिलते हैं और प्रोग्रामिंग का आनंद लेते हैं।

Source: https://habr.com/ru/post/In144646/


All Articles