JavaScript में ऑब्जेक्ट-ओरिएंटेड प्रोग्रामिंग (OOP) की क्षमताएँ शामिल हैं। OOP में आप "टेम्पलेट" (क्लास) से ऑब्जेक्ट (इंस्टेंस) बनाते हैं, ताकि उनमें कुछ खास डेटा और कार्यक्षमता शामिल हो। OOP के संदर्भ में डेटा वाली प्रॉपर्टी को फील्ड कहा जाता है और फंक्शन वाली प्रॉपर्टी को मेथड।
2015 में भाषा के स्पेसिफिकेशन में जोड़े जाने से पहले JavaScript में क्लास जैसी कोई चीज़ नहीं थी। लेकिन यह प्रोटोटाइप-आधारित इनहेरिटेंस की मदद से ऑब्जेक्ट-ओरिएंटेड प्रोग्रामिंग की सुविधा देती थी।
और आजकल class कीवर्ड उपलब्ध होने के बावजूद JavaScript अब भी एक प्रोटोटाइप-आधारित भाषा है।
यह समझने के लिए कि प्रोटोटाइप-आधारित भाषा होने का मतलब क्या है और JavaScript वास्तव में कैसे काम करती है, हम उस समय पर चलेंगे जब क्लास जैसी कोई चीज़ नहीं थी।
JavaScript में टेम्पलेट (क्लास) को एक साधारण फंक्शन के ज़रिए बनाया जाता है।
जब किसी फंक्शन को ऐसे टेम्पलेट के रूप में इस्तेमाल किया जाना हो, तो उसे कंस्ट्रक्टर फंक्शन कहते हैं। परंपरा यह है कि फंक्शन का नाम बड़े अक्षर से शुरू होना चाहिए।
जब कंस्ट्रक्टर फंक्शन को कॉल किया जाता है, तो new कीवर्ड के ज़रिए टेम्पलेट से इंस्टेंस (ऑब्जेक्ट) बनाए जाते हैं।
function Car() {
// ...
}
const myCar = new Car();
const yourCar = new Car();
यह ध्यान रखना ज़रूरी है कि JavaScript में इंस्टेंस बन जाने के बाद भी इंस्टेंस और कंस्ट्रक्टर फंक्शन का आपस में संबंध बना रहता है।
हर इंस्टेंस ऑब्जेक्ट में एक छिपी हुई आंतरिक प्रॉपर्टी होती है, जिसे भाषा के स्पेसिफिकेशन में [[prototype]] कहा जाता है।
इसमें कंस्ट्रक्टर फंक्शन के prototype की वैल्यू का रेफरेंस होता है।
जी हाँ, आपने ठीक पढ़ा, एक JavaScript फंक्शन में की-वैल्यू जोड़े हो सकते हैं, क्योंकि पर्दे के पीछे वह भी एक ऑब्जेक्ट है।
संक्षेप में:
prototype प्रॉपर्टी ही इंस्टेंस का प्रोटोटाइप बन जाती है।अक्सर आप चाहते हैं कि सभी बने हुए ऑब्जेक्ट (इंस्टेंस) में कुछ फील्ड हों, और ऑब्जेक्ट बनाते समय उन फील्ड के लिए कुछ शुरुआती वैल्यू भी दी जाएँ।
यह this कीवर्ड के ज़रिए किया जा सकता है।
कंस्ट्रक्टर फंक्शन के अंदर this उस नए ऑब्जेक्ट को दर्शाता है जो new के ज़रिए बनाया जाएगा।
जब कंस्ट्रक्टर फंक्शन को new के साथ कॉल किया जाता है, तो this अपने आप उससे लौटा दिया जाता है।
इसका मतलब है कि कंस्ट्रक्टर फंक्शन में this में फील्ड जोड़कर हम नए इंस्टेंस में फील्ड जोड़ सकते हैं।
function Car(color, weight) {
this.color = color;
this.weight = weight;
this.engineRunning = false;
}
const myCar = new Car('red', '2mt');
myCar.color;
// => 'red'
myCar.engineRunning;
// => false
मेथड कंस्ट्रक्टर फंक्शन की prototype प्रॉपर्टी के ज़रिए जोड़े जाते हैं।
किसी मेथड के अंदर आप this के ज़रिए इंस्टेंस के फील्ड तक पहुँच सकते हैं।
यह नीचे दिए सामान्य नियम के कारण काम करता है।
जब किसी फंक्शन को किसी ऑब्जेक्ट के मेथड के रूप में कॉल किया जाता है, तो उसका
thisउस ऑब्जेक्ट पर सेट हो जाता है जिस पर वह मेथड कॉल किया गया है। 1
function Car() {
this.engineRunning = false;
// ...
}
Car.prototype.startEngine = function () {
this.engineRunning = true;
};
Car.prototype.addGas = function (litre) {
// ...
};
const myCar = new Car();
myCar.startEngine();
myCar.engineRunning;
// => true
ऊपर के उदाहरण में myCar एक साधारण JavaScript ऑब्जेक्ट है, और अगर हम उसे देखें (जैसे ब्राउज़र कंसोल में), तो myCar ऑब्जेक्ट के अंदर सीधे startEngine नाम की ऐसी प्रॉपर्टी नहीं मिलेगी जिसमें कोई फंक्शन वैल्यू हो।
तो फिर ऊपर का कोड काम कैसे करता है?
इसे प्रोटोटाइप चेन कहते हैं।
जब आप किसी ऑब्जेक्ट की कोई प्रॉपर्टी (फील्ड या मेथड) पाने की कोशिश करते हैं, तो JavaScript सबसे पहले देखती है कि वह की सीधे उस ऑब्जेक्ट में ही मिलती है या नहीं।
अगर नहीं, तो वह मूल ऑब्जेक्ट की [[prototype]] प्रॉपर्टी जिस ऑब्जेक्ट को दर्शाती है, उसमें उस की को ढूँढती रहती है।
जैसा पहले बताया गया, हमारे उदाहरण में [[prototype]] कंस्ट्रक्टर फंक्शन की prototype प्रॉपर्टी की ओर इशारा करता है।
यहीं JavaScript को startEngine फंक्शन मिलता है, क्योंकि हमने उसे वहीं जोड़ा था।
function Car() {
// ...
}
Car.prototype.startEngine = function () {
// ...
};
और यह चेन वहीं खत्म नहीं होती।
Car.prototype की [[prototype]] प्रॉपर्टी (myCar.[[prototype]].[[prototype]]) Object.prototype को दर्शाती है (Object कंस्ट्रक्टर फंक्शन की prototype प्रॉपर्टी)।
इसमें ऐसे सामान्य मेथड होते हैं जो सभी JavaScript ऑब्जेक्ट के लिए उपलब्ध होते हैं, जैसे toString()।
इसलिए आप myCar.toString() कॉल कर सकते हैं, और वह मेथड मौजूद रहेगा, क्योंकि JavaScript उस मेथड को पूरी प्रोटोटाइप चेन में ढूँढती है।
ध्यान दीजिए कि प्रोटोटाइप चेन पर चलना सिर्फ तब होता है जब कोई वैल्यू पढ़ी जा रही हो। किसी इंस्टेंस ऑब्जेक्ट की प्रॉपर्टी सीधे सेट करना या हटाना सिर्फ उसी इंस्टेंस पर असर डालता है। अगर आप क्लास-आधारित इनहेरिटेंस वाली भाषा के आदी हैं, तो यह आपकी अपेक्षा के मुताबिक न लगे।
आजकल JavaScript में class कीवर्ड से क्लास बनाने की सुविधा है।
यह 2015 में भाषा के स्पेसिफिकेशन में जोड़ा गया था।
एक तरफ, यह सिंटैक्टिक शुगर देता है जिससे क्लास पढ़ना और लिखना आसान हो जाता है।
नया सिंटैक्स C++ या Java जैसी भाषाओं में क्लास लिखने के तरीके से ज़्यादा मिलता-जुलता है।
उन भाषाओं से आने वाले डेवलपर के लिए ढलना आसान हो जाता है।
दूसरी तरफ, क्लास सिंटैक्स ऐसे नए भाषा फीचर के रास्ते खोलता है जो प्रोटोटाइप सिंटैक्स में उपलब्ध नहीं हैं।
नए सिंटैक्स में क्लास को class कीवर्ड से बनाया जाता है। इसके बाद क्लास का नाम आता है, और फिर घुंघराले ब्रैकेट में क्लास बॉडी लिखी जाती है।
इस बॉडी में कंस्ट्रक्टर फंक्शन की परिभाषा होती है, यानी constructor नाम का एक खास मेथड।
यह फंक्शन प्रोटोटाइप सिंटैक्स के कंस्ट्रक्टर फंक्शन की तरह ही काम करता है।
क्लास बॉडी में सभी मेथड की परिभाषाएँ भी होती हैं।
मेथड का सिंटैक्स उस शॉर्टहैंड तरीके जैसा है जिसे हमने ऑब्जेक्ट के अंदर फंक्शन को वैल्यू के रूप में जोड़ते समय देखा था, देखिए ऑब्जेक्ट कॉन्सेप्ट।
class Car {
constructor(color, weight) {
this.color = color;
this.weight = weight;
this.engineRunning = false;
}
startEngine() {
this.engineRunning = true;
}
addGas(litre) {
// ...
}
}
const myCar = new Car();
myCar.startEngine();
myCar.engineRunning;
// => true
ध्यान रखिए कि पर्दे के पीछे JavaScript अब भी एक प्रोटोटाइप-आधारित भाषा है। ऊपर "प्रोटोटाइप सिंटैक्स" वाले हिस्से में हमने जिन तरीकों के बारे में सीखा, वे सब अब भी लागू होते हैं।
डिफॉल्ट रूप से JavaScript में सभी इंस्टेंस फील्ड पब्लिक होते हैं। इन्हें सीधे पढ़ा जा सकता है और इनमें वैल्यू असाइन की जा सकती है।
लेकिन एक स्थापित परंपरा है कि अंडरस्कोर से शुरू होने वाले फील्ड और मेथड को प्राइवेट माना जाना चाहिए। इन्हें क्लास के बाहर से कभी सीधे नहीं पढ़ना चाहिए।
प्राइवेट फील्ड के साथ कभी-कभी गेटर और सेटर भी होते हैं।
get और set कीवर्ड की मदद से आप ऐसे फंक्शन बना सकते हैं जो तब चलते हैं जब फंक्शन के नाम वाली कोई प्रॉपर्टी पढ़ी या असाइन की जाती है।
class Car {
constructor() {
this._mileage = 0;
}
get mileage() {
return this._mileage;
}
set mileage(value) {
throw new Error(`Mileage cannot be manipulated, ${value} is ignored.`);
// Just an example, usually you would not provide a setter in this case.
}
}
const myCar = new Car();
myCar.mileage;
// => 0
myCar.mileage = 100;
// => Error: Mileage cannot be manipulated, 100 is ignored.
this के उदाहरण - एक ऑब्जेक्ट मेथड के रूप में, MDN। https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/this#as_an_object_method (3 दिसंबर 2021 को संदर्भित) ↩
इस अभ्यास में आप एक विंडो आधारित कंप्यूटर सिस्टम का अनुकरण करेंगे। आप कुछ विंडो बनाएँगे जिन्हें खिसकाया जा सकता है और जिनका आकार बदला जा सकता है। नीचे दिया गया चित्र उन वैल्यू को दर्शाता है जिनके साथ आप आगे काम करेंगे।
<--------------------- screenSize.width --------------------->
^ ┌────────────────────────────────────────────────────────────┐
| │ │
| │ position.x, _ │
| │ position.y \ │
| │ \<----- size.width -----> │
| │ ^ *──────────────────────┐ │
| │ | │ title │ │
| │ | ├──────────────────────┤ │
screenSize.height │ | │ │ │
| │ size.height │ │ │
| │ | │ contents │ │
| │ | │ │ │
| │ | │ │ │
| │ v └──────────────────────┘ │
| │ │
| │ │
v └────────────────────────────────────────────────────────────┘
📣 अपने JavaScript कौशल की विस्तृत श्रृंखला का अभ्यास करने के लिए, कार्य 1 और 2 प्रोटोटाइप सिंटैक्स से और बाकी कार्य क्लास सिंटैक्स से हल करके देखिए।
Size नाम की एक क्लास (कंस्ट्रक्टर फंक्शन) बनाइए।
इसमें दो फील्ड width और height होने चाहिए, जो विंडो के वर्तमान आयाम संग्रहीत करते हैं।
कंस्ट्रक्टर फंक्शन को इन फील्ड के लिए शुरुआती वैल्यू लेनी चाहिए।
चौड़ाई पहले पैरामीटर के रूप में दी जाती है और ऊँचाई दूसरे के रूप में।
डिफॉल्ट चौड़ाई और ऊँचाई क्रमशः 80 और 60 होनी चाहिए।
इसके अलावा, एक मेथड resize(newWidth, newHeight) बनाइए जो नई चौड़ाई और ऊँचाई को पैरामीटर के रूप में लेता है और फील्ड को बदलकर उन्हें नए आकार के अनुसार कर देता है।
const size = new Size(1080, 764);
size.width;
// => 1080
size.height;
// => 764
size.resize(1920, 1080);
size.width;
// => 1920
size.height;
// => 1080
Position नाम की एक क्लास (कंस्ट्रक्टर फंक्शन) बनाइए जिसमें दो फील्ड x और y हों, जो विंडो के ऊपरी बाएँ कोने की वर्तमान आड़ी और खड़ी स्थिति संग्रहीत करते हैं।
कंस्ट्रक्टर फंक्शन को इन फील्ड के लिए शुरुआती वैल्यू लेनी चाहिए।
x की वैल्यू पहले पैरामीटर के रूप में दी जाती है और y की वैल्यू दूसरे के रूप में।
दोनों फील्ड के लिए डिफॉल्ट वैल्यू 0 होनी चाहिए।
स्थिति (0, 0) स्क्रीन का ऊपरी बाएँ कोना होती है। दाएँ जाने पर x की वैल्यू बढ़ती जाती है और नीचे जाने पर y की वैल्यू बढ़ती जाती है।
एक मेथड move(newX, newY) भी बनाइए जो नए x और y पैरामीटर लेता है और प्रॉपर्टी को बदलकर उन्हें नई स्थिति के अनुसार कर देता है।
const point = new Position();
point.x;
// => 0
point.y;
// => 0
point.move(100, 200);
point.x;
// => 100
point.y;
// => 200
निम्नलिखित फील्ड के साथ एक ProgramWindow क्लास बनाइए:
screenSize: Size टाइप की एक निश्चित वैल्यू रखता है, जिसकी width 800 और height 600 हैsize : Size टाइप की एक वैल्यू रखता है, जिसकी शुरुआती वैल्यू Size इंस्टेंस की डिफॉल्ट वैल्यू होती हैposition : Position टाइप की एक वैल्यू रखता है, जिसकी शुरुआती वैल्यू Position इंस्टेंस की डिफॉल्ट वैल्यू होती हैजब विंडो खोली (बनाई) जाती है, तो शुरू में उसका आकार और स्थिति हमेशा डिफॉल्ट होती है।
const programWindow = new ProgramWindow();
programWindow.screenSize.width;
// => 800
// Similar for the other fields.
एक छोटी बात: क्लास को ब्राउज़र वातावरण में मौजूद बिल्ट-इन Window क्लास से अलग बताने के लिए Window की जगह ProgramWindow नाम रखा गया है।
ProgramWindow क्लास में एक resize मेथड होना चाहिए।
यह इनपुट के रूप में Size टाइप का एक पैरामीटर लेता है और विंडो का आकार बताए गए आकार में बदलने की कोशिश करता है।
लेकिन नया आकार कुछ सीमाओं से बड़ा नहीं हो सकता।
x = 400, y = 300 है और आकार को height = 400, width = 300 करने का अनुरोध किया जाता है, तो विंडो का आकार height = 300, width = 300 कर दिया जाएगा, क्योंकि y दिशा में स्क्रीन इतनी बड़ी नहीं है कि यह अनुरोध पूरी तरह पूरा हो सके।const programWindow = new ProgramWindow();
const newSize = new Size(600, 400);
programWindow.resize(newSize);
programWindow.size.width;
// => 600
programWindow.size.height;
// => 400
आकार बदलने की सुविधा के अलावा, ProgramWindow क्लास में एक move मेथड भी होना चाहिए।
यह इनपुट के रूप में Position टाइप का एक पैरामीटर लेता है।
move मेथड resize जैसा ही है, लेकिन यह मेथड आकार के बजाय विंडो की स्थिति को माँगी गई वैल्यू पर सेट करता है।
resize की तरह ही नई स्थिति भी कुछ सीमाओं से आगे नहीं जा सकती।
x और y दोनों के लिए सबसे छोटी स्थिति 0 है।x = 250, y = 100 है और x = 600, y = 200 पर खिसकाने का अनुरोध किया जाता है, तो विंडो को x = 550, y = 200 पर खिसका दिया जाएगा, क्योंकि x दिशा में स्क्रीन इतनी बड़ी नहीं है कि यह अनुरोध पूरी तरह पूरा हो सके।const programWindow = new ProgramWindow();
const newPosition = new Position(50, 100);
programWindow.move(newPosition);
programWindow.position.x;
// => 50
programWindow.position.y;
// => 100
एक changeWindow फंक्शन बनाइए जो इनपुट के रूप में एक ProgramWindow इंस्टेंस लेता है और विंडो को बताए गए आकार और स्थिति में बदल देता है।
बदलाव लागू करने के बाद फंक्शन को वही ProgramWindow इंस्टेंस लौटाना चाहिए जो इसे दिया गया था।
विंडो की चौड़ाई 400 और ऊँचाई 300 होनी चाहिए और उसकी स्थिति x = 100, y = 150 होनी चाहिए।
const programWindow = new ProgramWindow();
changeWindow(programWindow);
programWindow.size.width;
// => 400
// Similar for the other fields.
Exercism पर साइन अप कीजिए और JavaScript को 37 कॉन्सेप्ट159 अभ्यास तथा असली इंसानों से मिलने वाली मेंटरिंग के साथ सीखिए और उसमें महारत हासिल कीजिए, वह भी बिल्कुल मुफ्त।