जावास्क्रिप्ट में ऑब्जेक्ट मॉडल के बारे में बहुत सारे शानदार लेख लिखे गए हैं। और इंटरनेट पर निजी वर्ग के सदस्यों को बनाने के विभिन्न तरीकों के बारे में योग्य विवरणों से भरा है। लेकिन संरक्षित तरीकों के बारे में - बहुत कम डेटा है। मैं इस अंतर को भरना चाहता हूं और बता सकता हूं कि आप शुद्ध जावास्क्रिप्ट ईसीएमएस्क्रिप्ट 5 में पुस्तकालयों के बिना संरक्षित तरीके कैसे बना सकते हैं।
इस लेख में:
स्रोत कोड और परीक्षणों के साथ गिट-हब रिपॉजिटरी से लिंक करें। संरक्षित वर्ग के सदस्यों की आवश्यकता क्यों है
संक्षेप में, तब
- कक्षा के संचालन को समझना और उसमें त्रुटियां खोजना आसान है। (आप तुरंत देख सकते हैं कि किस मामले में कक्षा के सदस्यों का उपयोग किया जाता है। यदि निजी है, तो आपको केवल इस वर्ग का विश्लेषण करना होगा, अच्छी तरह से, और यदि संरक्षित है, तो केवल यह और व्युत्पन्न वर्ग।)
- परिवर्तन का प्रबंधन करने के लिए आसान है। (उदाहरण के लिए, आप बिना किसी डर के निजी सदस्यों को हटा सकते हैं कि संपादन योग्य कक्षा के बाहर कुछ टूट जाएगा।)
- बग ट्रैकर में अनुप्रयोगों की संख्या कम हो जाती है, क्योंकि पुस्तकालय या नियंत्रण के उपयोगकर्ता हमारे "निजी" सदस्यों पर "सीना" कर सकते हैं, जिसे हमने कक्षा के नए संस्करण में निकालने या उनके काम के तर्क को बदलने का फैसला किया।
- और सामान्य तौर पर, संरक्षित वर्ग के सदस्य एक डिजाइन उपकरण होते हैं। यह अच्छा है और इसे अच्छी तरह से परीक्षण किया है।
आपको याद दिला दूं कि संरक्षित सदस्यों का मुख्य विचार वर्ग के उदाहरणों से उपयोगकर्ताओं के तरीकों और गुणों को छिपाना है, लेकिन साथ ही साथ व्युत्पन्न वर्गों को उन तक पहुंच प्रदान करने की अनुमति देता है।
टाइपस्क्रिप्ट का उपयोग करना संरक्षित विधियों को कॉल करने की अनुमति नहीं देगा, हालांकि, जावास्क्रिप्ट में संकलन के बाद, सभी निजी और संरक्षित सदस्य सार्वजनिक हो जाते हैं। उदाहरण के लिए, हम एक नियंत्रण या पुस्तकालय विकसित करते हैं जो उपयोगकर्ता अपनी साइटों या अनुप्रयोगों पर स्थापित करेंगे। ये उपयोगकर्ता वर्ग की अखंडता का उल्लंघन करते हुए संरक्षित सदस्यों के साथ जो कुछ भी करना चाहते हैं, कर सकेंगे। परिणामस्वरूप, हमारा बग ट्रैकर उन शिकायतों के साथ फूट रहा है जो हमारे पुस्तकालय या नियंत्रण ठीक से काम नहीं कर रहे हैं। हम इसे हल करने के लिए समय और प्रयास खर्च करते हैं -
"क्या यह है कि वस्तु ग्राहक की उस स्थिति में थी, जिसके कारण त्रुटि हुई!" । इसलिए, सभी के लिए जीवन को आसान बनाने के लिए, ऐसे संरक्षण की आवश्यकता है जो निजी और संरक्षित वर्ग के सदस्यों के अर्थ को बदलना संभव नहीं करेगा।
प्रश्न में विधि को समझने के लिए क्या आवश्यक है
संरक्षित वर्ग के सदस्यों को घोषित करने की विधि को समझने के लिए, आपको मजबूत ज्ञान की आवश्यकता है:
- जावास्क्रिप्ट में डिवाइस कक्षाएं और ऑब्जेक्ट।
- निजी वर्ग के सदस्य बनाने के तरीके (कम से कम क्लोजर के माध्यम से)।
- तरीके Object.defineProperty और Object.getOwnPropertyDescriptor
जावास्क्रिप्ट में डिवाइस मॉडल के बारे में, मैं सुझा सकता हूं, उदाहरण के लिए, एंड्री
अकिंशिन (
ड्रीमवल्कर ) द्वारा एक उत्कृष्ट लेख
"जेएस में समझना ओओपी [भाग संख्या 1]" ।
निजी संपत्तियों के बारे में एक अच्छी और, मेरी राय में, एमडीएन वेबसाइट पर
निजी वर्ग के सदस्यों को बनाने के लिए लगभग 4 विभिन्न तरीकों के
रूप में एक पूर्ण विवरण है।
Object.defineProperty विधि के रूप में, यह हमें गुण और विधियों को इन-लूप से छिपाने की अनुमति देगा, और, परिणामस्वरूप, क्रमबद्धता एल्गोरिदम से:
function MyClass(){ Object.defineProperty(MyClass.prototype, 'protectedNumber', { value: 12, enumerable: false }); this.publicNumber = 25; }; var obj1 = new MyClass(); for(var prop in obj1){ console.log('property:' prop);
इस तरह की छिपाव प्रदर्शन किया जाना चाहिए, लेकिन यह, निश्चित रूप से पर्याप्त नहीं है अभी भी विधि / संपत्ति को सीधे कॉल करने की संभावना है:
console.log(obj1.protectedNumber);
हेल्पर क्लास रक्षित
सबसे पहले, हमें संरक्षित क्षेत्र की आवश्यकता है, जो त्रुटि से विरासत में मिली है, और जिसे संरक्षित पद्धति या संपत्ति तक पहुंच नहीं होने पर फेंक दिया जाएगा।
function ProtectedError(){ this.message = "Encapsulation error, the object member you are trying to address is protected."; } ProtectedError.prototype = new Error(); ProtectedError.prototype.constructor = ProtectedError;
ES5 में संरक्षित वर्ग के सदस्यों को लागू करना
अब जब हमारे पास संरक्षित वर्ग है और हम समझते हैं कि क्या Object.defineProperty मूल्य के साथ गणना करता है: गलत, चलो एक आधार वर्ग के निर्माण का विश्लेषण करें जो संरक्षित व्युत्पन्न विधि को अपने सभी व्युत्पन्न वर्गों के साथ साझा करना चाहता है, लेकिन इसे बाकी सभी से छिपाएं:
function BaseClass(){ if (!(this instanceof BaseClass)) return new BaseClass(); var _self = this;
बेसक्लास क्लास कंस्ट्रक्टर का विवरण
शायद आप चेक से भ्रमित हो जाएंगे:
if (!(this instanceof BaseClass)) return new BaseClass();
यह परीक्षण एक "शौकिया" है। आप इसे हटा सकते हैं, इसका संरक्षित तरीकों से कोई लेना-देना नहीं है। हालांकि, मैं व्यक्तिगत रूप से इसे अपने कोड में छोड़ देता हूं, क्योंकि यह उन मामलों के लिए आवश्यक है जब कक्षा का उदाहरण सही ढंग से नहीं बनाया गया है, अर्थात्। कीवर्ड के बिना नया। उदाहरण के लिए, इस तरह:
var obj1 = BaseClass();
ऐसे मामलों में, जैसा आप चाहते हैं। उदाहरण के लिए, आप एक त्रुटि उत्पन्न कर सकते हैं:
if (!(this instanceof BaseClass)) throw new Error('Wrong instance creation. Maybe operator "new" was forgotten');
या आप बस सही ढंग से तुरंत बता सकते हैं, जैसा कि बेसक्लास में किया गया है।
अगला, हम _self चर में नए उदाहरण को सहेजते हैं (मुझे बाद में यह समझाने की आवश्यकता क्यों है)।
संरक्षितमेथोड नामक सार्वजनिक संपत्ति का विवरण
विधि में प्रवेश करते हुए, हम उस संदर्भ की जांच कहते हैं, जिस पर हमें बुलाया गया था। एक अलग विधि में जांच करना बेहतर है, उदाहरण के लिए, चेकअब, क्योंकि कक्षाओं के सभी संरक्षित तरीकों और गुणों में समान जांच की आवश्यकता होगी। तो, सबसे पहले, इस पर कॉल के संदर्भ प्रकार की जांच करें। यदि इसके पास बेसक्लास के अलावा कोई अन्य प्रकार है, तो यह प्रकार न तो बेसक्लास है और न ही इसका कोई डेरिवेटिव है। हम इस तरह के कॉल पर रोक लगाते हैं।
if(!(this instanceof BaseClass)) throw new ProtectedError();
यह कैसे हो सकता है? उदाहरण के लिए, इस तरह:
var b = new BaseClass(); var someObject = {}; b.protectedMethod.call(someObject);
व्युत्पन्न वर्गों के मामले में, इस उदाहरण बेसक्लास की अभिव्यक्ति सही होगी। लेकिन बेसक्लास के उदाहरणों के लिए, यह उदाहरण बेसक्लास अभिव्यक्ति सही होगा। इसलिए, बेसक्लास वर्ग के उदाहरणों को व्युत्पन्न वर्गों के उदाहरणों से अलग करने के लिए, हम कंस्ट्रक्टर की जांच करते हैं। यदि कंस्ट्रक्टर बेसक्लास से मेल खाता है, तो हमारे संरक्षितमैथ को बेसक्लास उदाहरण पर कहा जाता है, एक नियमित सार्वजनिक विधि की तरह:
var b = new BaseClass(); b.protectedMethod();
हम इस तरह के कॉल पर रोक लगाते हैं:
if(this.constructor === BaseClass) throw new ProtectedError();
इसके बाद संरक्षितमैथोड बंद विधि का कॉल आता है, जो वास्तव में, वह विधि है जिसकी हम रक्षा करते हैं। विधि के अंदर, यदि आपको बेसक्लास वर्ग के सदस्यों को संदर्भित करने की आवश्यकता है, तो आप इसे स्वयं के संग्रहीत उदाहरण का उपयोग करके कर सकते हैं। यह वही है जो सभी निजी / निजी तरीकों से वर्ग के सदस्यों तक पहुंच के लिए बनाया गया था। इसलिए, यदि आपको अपनी संरक्षित पद्धति या संपत्ति में वर्ग के सदस्यों तक पहुंचने की आवश्यकता नहीं है, तो आप _self चर नहीं बना सकते।
बेसक्लास वर्ग के अंदर एक संरक्षित पद्धति को कॉल करना
BaseClass क्लास के अंदर, SecureMethod को केवल नाम से ही एक्सेस किया जाना चाहिए, इसके माध्यम से नहीं। अन्यथा, संरक्षितमेथोड के अंदर हम यह भेद नहीं कर सकते हैं कि क्या हमें एक सार्वजनिक पद्धति के रूप में या एक वर्ग के भीतर से बुलाया गया था। इस मामले में, क्लोजर हमें बचाता है - संरक्षितमेथोड एक नियमित निजी पद्धति की तरह व्यवहार करता है, कक्षा के अंदर बंद हो जाता है और बेसक्लास फ़ंक्शन के दायरे में ही दिखाई देता है।
DerivedClass व्युत्पन्न वर्ग विवरण
अब एक व्युत्पन्न वर्ग को देखते हैं और इसे आधार वर्ग की संरक्षित पद्धति तक कैसे पहुँचा जा सकता है।
function DerivedClass(){ var _base = { protectedMethod: this.protectedMethod.bind(this) }; function checkAccess() { if (this.constructor === DerivedClass) throw new ProtectedError(); }
व्युत्पन्न वर्ग निर्माता विवरण
व्युत्पन्न वर्ग में, हम एक _base ऑब्जेक्ट बनाते हैं, जिसमें हम बेस क्लास की संरक्षित मेथोड विधि का संदर्भ देते हैं, जो मानक वर्ग विधि के माध्यम से व्युत्पन्न वर्ग के संदर्भ में बंद है। इसका मतलब है कि _base.protectedMethod () को कॉल करना; प्रोटेक्टमैथोड के अंदर यह _base ऑब्जेक्ट नहीं है, बल्कि DerivedClass क्लास का एक उदाहरण है।
संरक्षितमेथोड विधि का वर्णन डेरेवंडक्लास के अंदर
DerivedClass वर्ग में, संरक्षित विधि को उसी तरीके से घोषित करना आवश्यक है जिस तरह से हमने बेस क्लास में Object.defineProperty के माध्यम से किया था और चेक एक्सेस विधि को कॉल करके या विधि में सीधे जाँच करके उसमें एक्सेस की जाँच करें:
Object.defineProperty(DerivedClass.prototype, 'protectedMethod', { enumerable: false, configurable: false, value: function(){ if(this.constructor === DerivedClass) throw new ProtectedError() return _base.protectedMethod(); } });
हम जांच करते हैं -
"लेकिन क्या हमें एक साधारण सार्वजनिक पद्धति कहा जाता है?" DerivedClass वर्ग के उदाहरणों के लिए, निर्माणकर्ता DerivedClass के बराबर होगा। यदि ऐसा है, तो एक त्रुटि उत्पन्न करें। अन्यथा, हम इसे बेस क्लास में भेजते हैं और यह पहले से ही अन्य सभी जाँच करेगा।
इसलिए, व्युत्पन्न वर्ग में, हमारे दो कार्य हैं। एक Object.defineProperty के माध्यम से घोषित किया गया है और DerivedClass व्युत्पन्न वर्गों के लिए आवश्यक है। यह सार्वजनिक है और इसलिए इसमें सार्वजनिक कॉलों को प्रतिबंधित करने वाला चेक है। दूसरी विधि _base ऑब्जेक्ट में स्थित है, जो DerivedClass वर्ग के अंदर बंद है और इसलिए बाहर से किसी को दिखाई नहीं देता है और इसका उपयोग सभी DerivedClass विधियों से सुरक्षित विधि तक पहुंचने के लिए किया जाता है।
संपत्ति की सुरक्षा
गुणों के साथ, काम थोड़ा अलग तरीके से होता है। बेसक्लास में गुणों को हमेशा की तरह Object.defineProperty के माध्यम से परिभाषित किया जाता है, केवल गेटर्स में और आपको हमारे चेक को जोड़ने की आवश्यकता है। कॉल चेक करें असफल:
function BaseClass(){ function checkAccess(){ ... } var _protectedProperty; Object.defineProperty(this, 'protectedProperty', { get: function () { checkAccess.call(this); return _protectedProperty; }, set: function (value) { checkAccess.call(this); _protectedProperty = value; }, enumerable: false, configurable: false }); }
बेसक्लास वर्ग के अंदर, संरक्षित संपत्ति इसके माध्यम से नहीं, बल्कि बंद चर _protectedProperty तक पहुंच जाती है। अगर हमारे लिए यह महत्वपूर्ण है कि बेसक्लास वर्ग के अंदर संपत्ति का उपयोग करते समय गेट्टर और सेटर काम करते हैं, तो हमें निजी तरीकों को बनाने की आवश्यकता होती है GetProtectedPropety और setProtectedProperty, जिसके अंदर कोई चेक नहीं होगा, और उन्हें पहले से ही बुलाया जाना चाहिए।
function BaseClass(){ function checkAccess(){ ... } var _protectedProperty; Object.defineProperty(this, 'protectedProperty', { get: function () { checkAccess.call(this); return getProtectedProperty(); }, set: function (value) { checkAccess.call(this); setProtectedProperty(value); }, enumerable: false, configurable: false }); function getProtectedProperty(){
व्युत्पन्न वर्गों में, गुणों के साथ काम करना थोड़ा अधिक जटिल है, क्योंकि संपत्ति को संदर्भ से नहीं बदला जा सकता है। इसलिए, हम बेस क्लास की संपत्ति से प्राप्त करने वाले और सेटर के लिए मानक Object.getOwnPropertyDescriptor विधि का उपयोग करेंगे, क्योंकि फ़ंक्शन को कॉल संदर्भ को बदलने के लिए पहले से ही उपयोग किया जा सकता है:
function DerivedClass(){ function checkAccess(){ ... } var _base = { protectedMethod: _self.protectedMethod.bind(_self), }; var _baseProtectedPropertyDescriptor = Object.getOwnPropertyDescriptor(_self, 'protectedProperty');
वंशानुक्रम विवरण
और आखिरी चीज जिस पर मैं टिप्पणी करना चाहूंगा वह बेसक्लास से डेरिवेक्लास की विरासत है। जैसा कि आप जानते हैं, DerivedClass.prototype = new BaseClass (); न केवल एक प्रोटोटाइप बनाता है, बल्कि इसकी निर्माता संपत्ति को भी फिर से लिखता है। इस वजह से, DerivedClass के प्रत्येक उदाहरण के लिए, कंस्ट्रक्टर संपत्ति BaseClass के बराबर हो जाती है। इसे ठीक करने के लिए, आमतौर पर एक प्रोटोटाइप बनाने के बाद, निर्माणकर्ता की संपत्ति को फिर से लिखना:
DerivedClass.prototype = new BaseClass(); DerivedClass.prototype.constructor = DerivedClass;
हालाँकि, ताकि कोई भी हमारे बाद इस संपत्ति को दोबारा न लिखे, हम उसी Object.defineProperty का उपयोग करते हैं। विन्यास योग्य: गलत संपत्ति फिर से संपत्ति को ओवरराइड होने से रोकती है:
DerivedClass.prototype = new BaseClass(); Object.defineProperty(DerivedClass.prototype, 'constructor', { value : DerivedClass, configurable: false });