जावास्क्रिप्ट का उपयोग कैसे करें Object.defineProperty


183

मैंने इस Object.definePropertyपद्धति का उपयोग करने के लिए चारों ओर देखा , लेकिन कुछ भी अच्छा नहीं मिला।

किसी ने मुझे कोड का यह टुकड़ा दिया :

Object.defineProperty(player, "health", {
    get: function () {
        return 10 + ( player.level * 15 );
    }
})

लेकिन मुझे यह समझ में नहीं आता है। मुख्य रूप से, वह getहै जो मुझे नहीं मिल सकता है (इरादा इरादा)। यह कैसे काम करता है?


1
developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/… यह यहाँ एक उत्कृष्ट ट्यूटोरियल है।
Martian2049

जवाबों:


498

चूँकि आपने एक समान प्रश्न पूछा था , इसलिए इसे चरण दर चरण लेते हैं। यह थोड़ा लंबा है, लेकिन इसे लिखने में मैंने जितना खर्च किया है, उससे बहुत अधिक समय बच सकता है:

प्रॉपर्टी एक OOP फीचर है जो क्लाइंट कोड के साफ पृथक्करण के लिए बनाया गया है। उदाहरण के लिए, कुछ ई-शॉप में आपके पास इस तरह की वस्तुएं हो सकती हैं:

function Product(name,price) {
  this.name = name;
  this.price = price;
  this.discount = 0;
}

var sneakers = new Product("Sneakers",20); // {name:"Sneakers",price:20,discount:0}
var tshirt = new Product("T-shirt",10);  // {name:"T-shirt",price:10,discount:0}

फिर अपने ग्राहक कोड (ई-शॉप) में, आप अपने उत्पादों में छूट जोड़ सकते हैं:

function badProduct(obj) { obj.discount+= 20; ... }
function generalDiscount(obj) { obj.discount+= 10; ... }
function distributorDiscount(obj) { obj.discount+= 15; ... }

बाद में, ई-शॉप के मालिक को एहसास हो सकता है कि छूट 80% से अधिक नहीं हो सकती है। अब आपको ग्राहक कोड में छूट संशोधन की हर जगह खोजने और एक पंक्ति जोड़ने की आवश्यकता है

if(obj.discount>80) obj.discount = 80;

फिर ई-शॉप के मालिक अपनी रणनीति को और बदल सकते हैं, जैसे "यदि ग्राहक पुनर्विक्रेता है, तो अधिकतम छूट 90% हो सकती है" । और आपको फिर से कई स्थानों पर परिवर्तन करने की आवश्यकता है और साथ ही आपको इन पंक्तियों को बदलने के लिए याद रखना होगा जब भी रणनीति बदली जाती है। यह एक खराब डिजाइन है। इसीलिए एनकैप्सुलेशन OOP का मूल सिद्धांत है। अगर कंस्ट्रक्टर इस तरह था:

function Product(name,price) {
  var _name=name, _price=price, _discount=0;
  this.getName = function() { return _name; }
  this.setName = function(value) { _name = value; }
  this.getPrice = function() { return _price; }
  this.setPrice = function(value) { _price = value; }
  this.getDiscount = function() { return _discount; }
  this.setDiscount = function(value) { _discount = value; } 
}

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

function Product(name,price) {
  this.name = name;
  this.price = price;
//this.discount = 0; // <- remove this line and refactor with the code below
  var _discount; // private member
  Object.defineProperty(this,"discount",{
    get: function() { return _discount; },
    set: function(value) { _discount = value; if(_discount>80) _discount = 80; }
  });
}

// the client code
var sneakers = new Product("Sneakers",20);
sneakers.discount = 50; // 50, setter is called
sneakers.discount+= 20; // 70, setter is called
sneakers.discount+= 20; // 80, not 90!
alert(sneakers.discount); // getter is called

अंतिम लेकिन एक पंक्ति पर ध्यान दें: सही छूट मूल्य के लिए जिम्मेदारी ग्राहक कोड (ई-शॉप परिभाषा) से उत्पाद परिभाषा में स्थानांतरित की गई थी। उत्पाद अपने डेटा सदस्यों को लगातार बनाए रखने के लिए जिम्मेदार है। अच्छा डिजाइन है (मोटे तौर पर कहा जाता है) अगर कोड हमारे विचारों के समान काम करता है।

गुणों के बारे में इतना। लेकिन जावास्क्रिप्ट शुद्ध ऑब्जेक्ट-ओरिएंटेड भाषाओं जैसे C # से अलग है और सुविधाओं को अलग तरह से कोड करता है:

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

जावास्क्रिप्ट में , मानक गुण (ऊपर वर्णित गेट्टर और सेटर के साथ डेटा सदस्य) एक्सेसर डिस्क्रिप्टर द्वारा परिभाषित किए गए हैं (आपके प्रश्न में लिंक में)। विशेष रूप से, आप डेटा डिस्क्रिप्टर का उपयोग कर सकते हैं (ताकि आप एक ही प्रॉपर्टी पर मूल्य और सेट का उपयोग न कर सकें ):

  • एक्सेसर डिस्क्रिप्टर = गेट + सेट (ऊपर उदाहरण देखें)
    • प्राप्त एक समारोह होना चाहिए; संपत्ति को पढ़ने में इसका वापसी मूल्य उपयोग किया जाता है; यदि निर्दिष्ट नहीं किया गया है, तो डिफ़ॉल्ट अपरिभाषित है , जो एक फ़ंक्शन की तरह व्यवहार करता है जो अपरिभाषित देता है
    • सेट एक फ़ंक्शन होना चाहिए; इसका मान संपत्ति के मूल्य को निर्दिष्ट करने में आरएचएस से भरा है; यदि निर्दिष्ट नहीं है, तो डिफ़ॉल्ट अपरिभाषित है , जो एक खाली फ़ंक्शन की तरह व्यवहार करता है
  • डेटा डिस्क्रिप्टर = मान + लेखन (नीचे उदाहरण देखें)
    • मान डिफ़ॉल्ट अपरिभाषित ; यदि लेखन योग्य , विन्यास योग्य और गणना योग्य (नीचे देखें) सत्य हैं, तो संपत्ति एक साधारण डेटा फ़ील्ड की तरह व्यवहार करती है
    • लेखन योग्य - डिफ़ॉल्ट असत्य ; यदि सही नहीं है , तो संपत्ति केवल पढ़ी जाती है; लिखने का प्रयास त्रुटि के बिना अनदेखा है *!

दोनों विवरणक में ये सदस्य हो सकते हैं:

  • विन्यास योग्य - डिफ़ॉल्ट गलत ; यदि सही नहीं है, तो संपत्ति को हटाया नहीं जा सकता है; हटाने का प्रयास त्रुटि के बिना अनदेखा है *!
  • enumerable - default false ; अगर यह सच है, तो इसमें इसकी पुनरावृत्ति होगीfor(var i in theObject); यदि गलत है, तो इसे पुनरावृत्त नहीं किया जाएगा, लेकिन यह अभी भी सार्वजनिक रूप से सुलभ है

* जब तक सख्त मोड में नहीं होता है - उस स्थिति में JS टाइप टाइप के साथ निष्पादन बंद कर देता है जब तक कि इसे ट्राइ -कैच ब्लॉक में न पकड़ा जाए

इन सेटिंग्स को पढ़ने के लिए, उपयोग करें Object.getOwnPropertyDescriptor()

उदाहरण के द्वारा जानें:

var o = {};
Object.defineProperty(o,"test",{
  value: "a",
  configurable: true
});
console.log(Object.getOwnPropertyDescriptor(o,"test")); // check the settings    

for(var i in o) console.log(o[i]); // nothing, o.test is not enumerable
console.log(o.test); // "a"
o.test = "b"; // o.test is still "a", (is not writable, no error)
delete(o.test); // bye bye, o.test (was configurable)
o.test = "b"; // o.test is "b"
for(var i in o) console.log(o[i]); // "b", default fields are enumerable

यदि आप क्लाइंट कोड को धोखा देने की अनुमति नहीं देना चाहते हैं, तो आप ऑब्जेक्ट को तीन स्तरों तक सीमित कर सकते हैं:

  • Object.preventExtensions (yourObject) नए गुणों को अपने Object में जोड़ने से रोकता हैObject.isExtensible(<yourObject>)जाँच करने के लिएउपयोगकरें कि क्या विधि का उपयोग वस्तु पर किया गया था। रोकथाम उथली है (नीचे पढ़ें)।
  • Object.seal (yourObject) ऊपर के समान और गुणों को हटाया नहीं जा सकता (प्रभावी रूपconfigurable: falseसे सभी गुणों के लिएसेट)। Object.isSealed(<yourObject>)ऑब्जेक्ट पर इस सुविधा का पता लगाने के लिएउपयोग करें। सील उथली है (नीचे पढ़ें)।
  • ऑब्जेक्ट.फ्रीज (आपका ऑबजेक्ट) जैसा कि ऊपर दिया गया है और गुणों को नहीं बदला जा सकता है (प्रभावी रूपwritable: falseसे डेटा डिस्क्रिप्टर के साथ सभी गुणों परसेटहोता है)। सेटर की लिखने योग्य संपत्ति प्रभावित नहीं होती है (क्योंकि इसमें एक नहीं है)। फ्रीज उथला है : इसका मतलब है कि अगर संपत्ति वस्तु है, तो इसके गुण जमे हुए नहीं हैं (यदि आप चाहें, तो आपको "डीप फ्रीज" जैसा कुछ करना चाहिए, डीप कॉपी - क्लोनिंग के समान)। इसकाObject.isFrozen(<yourObject>)पता लगाने के लिएउपयोग करें।

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


यह भाग स्पष्ट है। function Product(name,price) { this.name = name; this.price = price; var _discount; // private member Object.defineProperty(this,"discount",{ get: function() { return _discount; }, set: function(value) { _discount = value; if(_discount>80) _discount = 80; } }); } var sneakers = new Product("Sneakers",20); sneakers.discount = 50; // 50, setter is called sneakers.discount+= 20; // 70, setter is called sneakers.discount+= 20; // 80, not 90! alert(sneakers.discount); // getter is called
अबू

27

getएक फ़ंक्शन है जिसे कहा जाता है जब आप मान को पढ़ने की कोशिश करते हैं player.health, जैसे:

console.log(player.health);

यह प्रभावी रूप से बहुत अलग नहीं है:

player.getHealth = function(){
  return 10 + this.level*15;
}
console.log(player.getHealth());

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

player.health = 5; // Doesn't do anything, since there is no set function defined

एक बहुत ही सरल उदाहरण:

var player = {
  level: 5
};

Object.defineProperty(player, "health", {
  get: function() {
    return 10 + (player.level * 15);
  }
});

console.log(player.health); // 85
player.level++;
console.log(player.health); // 100

player.health = 5; // Does nothing
console.log(player.health); // 100


यह केवल एक फ़ंक्शन की तरह है जिसे आपको ()कॉल करने के लिए वास्तव में उपयोग करने की आवश्यकता नहीं है ... मुझे समझ में नहीं आता कि जब उन्होंने इस चीज का आविष्कार किया था तो क्या विचार था। कार्य पूरी तरह से समान हैं: jsbin.com/bugipi/edit?js,console,output
vsync

15

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

var employee = {
    firstName: "Jameel",
    lastName: "Moideen"
};
employee.toString=function () {
    return this.firstName + " " + this.lastName;
};
console.log(employee.toString());

आपको आउटपुट मिलेगा: जमील मोइदीन

मैं ऑब्जेक्ट पर डिफाइनप्रॉपर्टी का उपयोग करके समान कोड को बदलने जा रहा हूं

var employee = {
    firstName: "Jameel",
    lastName: "Moideen"
};
Object.defineProperty(employee, 'toString', {
    value: function () {
        return this.firstName + " " + this.lastName;
    },
    writable: true,
    enumerable: true,
    configurable: true
});
console.log(employee.toString());

पहला पैरामीटर ऑब्जेक्ट का नाम है और फिर दूसरा पैरामीटर उस संपत्ति का नाम है जिसे हम जोड़ रहे हैं, हमारे मामले में यह स्ट्रींग है और फिर अंतिम पैरामीटर json ऑब्जेक्ट है जिसका मूल्य एक फ़ंक्शन और तीन पैरामीटर राइट करने योग्य है, enumerable और विन्यास योग्य। अब मैं सिर्फ सब कुछ सच घोषित करता हूं।

यदि आप उदाहरण चलाते हैं तो आपको आउटपुट मिलेगा: जमील मोइदीन

आइए समझते हैं कि हमें तीन गुणों की आवश्यकता क्यों है जैसे कि लेखन योग्य, गणना करने योग्य और कॉन्फ़िगर करने योग्य।

लिखने योग्य

जावास्क्रिप्ट के बहुत ही कष्टप्रद भाग में से एक है, यदि आप उदाहरण के लिए संपत्ति को कुछ और के लिए बदल देते हैं

यहां छवि विवरण दर्ज करें

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

गणनीय

यदि आप ऑब्जेक्ट के अंदर सभी कुंजियों को प्रिंट करते हैं, तो आप सभी गुणों को देख सकते हैं, जिसमें स्ट्रींग शामिल हैं।

console.log(Object.keys(employee));

यहां छवि विवरण दर्ज करें

यदि आप झूठे के लिए अनगिनत सेट करते हैं, तो आप बाकी सभी से संपत्ति को छिपा सकते हैं। यदि इसे फिर से चलाते हैं तो आपको पहला नाम, अंतिम नाम मिलेगा

विन्यास

यदि बाद में किसी ने वस्तु को बाद में फिर से परिभाषित किया, उदाहरण के लिए यह सच है और इसे चलाने के लिए। आप देख सकते हैं कि संपत्ति फिर से आ गई।

var employee = {
    firstName: "Jameel",
    lastName: "Moideen"
};
Object.defineProperty(employee, 'toString', {
    value: function () {
        return this.firstName + " " + this.lastName;
    },
    writable: false,
    enumerable: false,
    configurable: true
});

//change enumerable to false
Object.defineProperty(employee, 'toString', {

    enumerable: true
});
employee.toString="changed";
console.log(Object.keys(employee));

यहां छवि विवरण दर्ज करें

आप इस व्यवहार को झूठा करने के लिए विन्यास द्वारा प्रतिबंधित कर सकते हैं।

इस जानकारी का मूल संदर्भ मेरे निजी ब्लॉग से है


1
मुझे लगता है कि आपके पास यह आपके ब्लॉग पर था और बस इसे यहां चिपकाया गया था, लेकिन कम से कम भविष्य के लिए यह जानते हैं: एसके के लिए स्क्रेंप लोकप्रिय नहीं हैं। आप इसे आज़माने के लिए कोड को कॉपी नहीं कर सकते हैं और कोड खोज इंजन या सहायक तकनीक द्वारा नहीं देखा जाएगा।
डोमिनोज

@JacqueGoupil आप कर रहे हैं सही है.मैं स्क्रीन शॉट के बजाय जोड़ने कोड से अपडेट हो जाएगा
कोड-ईज़ी

3

मूल रूप से, definePropertyएक विधि है जो 3 मापदंडों में ले जाती है - एक वस्तु, एक संपत्ति और एक विवरणक। क्या इस विशेष कॉल में हो रहा है "health"की संपत्ति playerवस्तु 10 प्लस 15 बार है कि खिलाड़ी वस्तु के स्तर को असाइन किया गया हो रही है।


0

हां, सेटअप सेटर और गटर के लिए कोई और कार्य नहीं है, यह मेरा उदाहरण है Object.defineProperty (obj, name, func)

var obj = {};
['data', 'name'].forEach(function(name) {
    Object.defineProperty(obj, name, {
        get : function() {
            return 'setter & getter';
        }
    });
});


console.log(obj.data);
console.log(obj.name);

0

Object.defineProperty () एक वैश्विक फ़ंक्शन है..जो फ़ंक्शन के अंदर उपलब्ध नहीं है जो ऑब्जेक्ट को अन्यथा घोषित करता है। आपको इसे सांख्यिकीय रूप से उपयोग करना होगा ...


0

सारांश:

Object.defineProperty(player, "health", {
    get: function () {
        return 10 + ( player.level * 15 );
    }
});

Object.definePropertyखिलाड़ी वस्तु पर एक नई संपत्ति बनाने के लिए प्रयोग किया जाता है। Object.definePropertyएक फ़ंक्शन है जो मूल रूप से JS रनटाइम environemnt में मौजूद है और निम्नलिखित तर्क लेता है:

Object.defineProperty(obj, prop, descriptor)

  1. वह वस्तु जिस पर हम एक नई संपत्ति को परिभाषित करना चाहते हैं
  2. नई प्रॉपर्टी का नाम जिसे हम परिभाषित करना चाहते हैं
  3. वर्णनात्मक वस्तु

वर्णनात्मक वस्तु दिलचस्प हिस्सा है। यहाँ हम निम्नलिखित बातों को परिभाषित कर सकते हैं:

  1. विन्यास योग्य <boolean> : यदि true संपत्ति विवरणक को बदला जा सकता है और संपत्ति को वस्तु से हटाया जा सकता है। यदि विन्यास योग्य falseविवरणक गुण है, जो Object.definePropertyपरिवर्तित नहीं किया जा सकता है।
  2. लिखने योग्य <boolean> : यदि trueअसाइनमेंट ऑपरेटर का उपयोग करके संपत्ति को अधिलेखित किया जा सकता है।
  3. गणना करने योग्य <boolean> : यदि true संपत्ति एक for...inलूप में अधिक हो सकती है । Object.keysफ़ंक्शन का उपयोग करते समय भी कुंजी मौजूद होगी। यदि संपत्ति है, तो falseवे एक for..inलूप का उपयोग करने पर पुनरावृत्त नहीं होंगे और उपयोग करते समय नहीं दिखाएंगे Object.keys
  4. get <function> : एक फ़ंक्शन जिसे जब भी कहा जाता है संपत्ति की आवश्यकता होती है। प्रत्यक्ष मूल्य देने के बजाय इस फ़ंक्शन को कहा जाता है और लौटाए गए मूल्य को संपत्ति के मूल्य के रूप में दिया जाता है
  5. सेट <function> : एक फ़ंक्शन जिसे जब भी कहा जाता है संपत्ति निर्दिष्ट की जाती है। प्रत्यक्ष मान सेट करने के बजाय इस फ़ंक्शन को कहा जाता है और लौटाए गए मूल्य का उपयोग संपत्ति के मूल्य को निर्धारित करने के लिए किया जाता है।

उदाहरण:

const player = {
  level: 10
};

Object.defineProperty(player, "health", {
  configurable: true,
  enumerable: false,
  get: function() {
    console.log('Inside the get function');
    return 10 + (player.level * 15);
  }
});

console.log(player.health);
// the get function is called and the return value is returned as a value

for (let prop in player) {
  console.log(prop);
  // only prop is logged here, health is not logged because is not an iterable property.
  // This is because we set the enumerable to false when defining the property
}


0

import { CSSProperties } from 'react'
import { BLACK, BLUE, GREY_DARK, WHITE } from '../colours'

export const COLOR_ACCENT = BLUE
export const COLOR_DEFAULT = BLACK
export const FAMILY = "'Segoe UI', sans-serif"
export const SIZE_LARGE = '26px'
export const SIZE_MEDIUM = '20px'
export const WEIGHT = 400

type Font = {
  color: string,
  size: string,
  accent: Font,
  default: Font,
  light: Font,
  neutral: Font,
  xsmall: Font,
  small: Font,
  medium: Font,
  large: Font,
  xlarge: Font,
  xxlarge: Font
} & (() => CSSProperties)

function font (this: Font): CSSProperties {
  const css = {
    color: this.color,
    fontFamily: FAMILY,
    fontSize: this.size,
    fontWeight: WEIGHT
  }
  delete this.color
  delete this.size
  return css
}

const dp = (type: 'color' | 'size', name: string, value: string) => {
  Object.defineProperty(font, name, { get () {
    this[type] = value
    return this
  }})
}

dp('color', 'accent', COLOR_ACCENT)
dp('color', 'default', COLOR_DEFAULT)
dp('color', 'light', COLOR_LIGHT)
dp('color', 'neutral', COLOR_NEUTRAL)
dp('size', 'xsmall', SIZE_XSMALL)
dp('size', 'small', SIZE_SMALL)
dp('size', 'medium', SIZE_MEDIUM)

export default font as Font


0

एक वस्तु पर सीधे एक नई संपत्ति को परिभाषित करता है, या किसी वस्तु पर एक मौजूदा संपत्ति को संशोधित करता है, और वस्तु को वापस करता है।

नोट: आप इस विधि को सीधे ऑब्जेक्ट कंस्ट्रक्टर पर टाइप ऑब्जेक्ट के एक उदाहरण के बजाय कहते हैं।

   const object1 = {};
   Object.defineProperty(object1, 'property1', {
      value: 42,
      writable: false, //If its false can't modify value using equal symbol
      enumerable: false, // If its false can't able to get value in Object.keys and for in loop
      configurable: false //if its false, can't able to modify value using defineproperty while writable in false
   });

यहां छवि विवरण दर्ज करें

संपत्ति को परिभाषित करने के बारे में सरल व्याख्या।

उदाहरण कोड: https://jsfiddle.net/manoj_antony32/pu5n61fs/


हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.