2013-08-30 15 views
128

मैंने Object.defineProperty विधि का उपयोग करने के तरीके के बारे में देखा, लेकिन कुछ भी सभ्य नहीं मिला।जावास्क्रिप्ट का उपयोग कैसे करें Object.defineProperty

मुझे किसी ने this snippet of code दिया:

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

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

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

प्राप्त के विपरीत सेट है:

console.log(player.health); 

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

+0

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

उत्तर

386

चूंकि आपने similar question से पूछा है, चलिए इसे चरण-दर-चरण लेते हैं। यह थोड़ा लंबा है, लेकिन यह आपको लिखने पर खर्च करने से कहीं अधिक समय बचा सकता है:

संपत्ति क्लाइंट कोड के स्वच्छ अलगाव के लिए डिज़ाइन की गई एक ओओपी सुविधा है। उदाहरण के लिए, कुछ ई-दुकान में आप इस तरह की वस्तुओं हो सकता है:

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% " हो सकता है। और आपको फिर से कई स्थानों पर परिवर्तन करने की आवश्यकता है और साथ ही आपको रणनीतियों को बदलने के लिए इन पंक्तियों को बदलने के लिए याद रखना होगा। यह एक खराब डिजाइन है। यही कारण है कि encapsulation ओओपी का मूल सिद्धांत है।अगर निर्माता इस तरह था:

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) तरीके बदल सकते हैं। समस्या यह है कि अधिकांश सदस्य सामान्य चर की तरह व्यवहार करते हैं, केवल छूट को विशेष देखभाल की आवश्यकता होती है। लेकिन अच्छे डिज़ाइन को कोड को विस्तारित रखने के लिए प्रत्येक डेटा सदस्य के encapsulation की आवश्यकता होती है। तो आपको बहुत सारे कोड जोड़ने की ज़रूरत है जो कुछ भी नहीं करता है। यह भी एक खराब डिजाइन है, बॉयलरप्लेट एंटीपाटरर्न। कभी-कभी आप फ़ील्ड को बाद में तरीकों से दोबारा नहीं कर सकते हैं (eshop कोड बड़ा हो सकता है या कुछ तृतीय-पक्ष कोड पुराने संस्करण पर निर्भर हो सकता है), इसलिए बॉयलरप्लेट यहां कम बुराई है। लेकिन फिर भी, यह बुराई है। यही कारण है कि कई भाषाओं में गुण पेश किए गए थे। आप मूल कोड रख सकता है, बस 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 

नोट पिछले लेकिन एक लाइन दुकान परिभाषा) उत्पाद परिभाषा के लिए। उत्पाद अपने डेटा सदस्यों को सुसंगत रखने के लिए ज़िम्मेदार है। अच्छा डिजाइन (लगभग कहा जाता है) अगर कोड हमारे विचारों के समान काम करता है।

गुणों के बारे में बहुत कुछ।

सी # में, गुण में खेतों बदलने ताकि सार्वजनिक क्षेत्रों Auto-Implemented Properties के रूप में कोडित किया जाना चाहिए अपने कोड में इस्तेमाल किया जा सकता है, तो एक breaking change है,: लेकिन जावास्क्रिप्ट सी # की तरह शुद्ध वस्तु उन्मुख भाषाओं और कोड अलग तरह से सुविधाओं से अलग है अलग संकलित ग्राहक।

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

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

दोनों वर्णनकर्ता इन सदस्यों हो सकता है:

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

* जब तक strict mode में - उस मामले में जे एस बंद हो जाता है लेखन त्रुटि के साथ निष्पादन जब तक यह try-catch block

में फंस जाता है इन पढ़ने के लिए सेटिंग्स, Object.getOwnPropertyDescriptor() का उपयोग करें।

उदाहरण से जानें:

  • Object.preventExtensions(yourObject) रोकता है:

    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 
    

    आप ग्राहक कोड इस तरह धोखा अनुमति देने के लिए नहीं करना चाहते हैं, तो आप कारावास के तीन स्तरों से वस्तु सीमित कर सकते हैं नई गुण में जोड़ा जाएगा yourObject। ऑब्जेक्ट पर विधि का उपयोग किया गया था या नहीं, यह जांचने के लिए Object.isExtensible(<yourObject>) का उपयोग करें। रोकथाम उथले (नीचे पढ़ें) है।

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

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

21

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

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

+0

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

+0

मैं एक उत्तर कैसे पसंदीदा कर सकता हूं ??? –

2

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

0

हाँ कोई और अधिक समारोह सेटअप सेटर & गेटर के लिए विस्तार यह मेरा उदाहरण है Object.defineProperty (obj, नाम, समारोह)

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() एक वैश्विक function..Its है समारोह जिसमें यह घोषणा की वस्तु यह स्थिर का उपयोग करने के otherwise.You'll ...

7

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

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

आप के रूप में आउटपुट मिल जाएगा: जमील Moideen

मैं वस्तु

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()); 

पहले पैरामीटर पर defineProperty उपयोग करके एक ही कोड को बदलने के लिए जा रहा हूँ का नाम है ऑब्जेक्ट और फिर दूसरा पैरामीटर उस संपत्ति का नाम है जिसे हम जोड़ रहे हैं, हमारे मामले में यह स्ट्रिंग है और फिर अंतिम पैरामीटर जेसन ऑब्जेक्ट है जिसमें एक फ़ंक्शन होने वाला मान और तीन पैरामीटर लिखने योग्य, गणनात्मक और सह nfigurable.ठीक है अब मैंने सबकुछ सत्य के रूप में घोषित किया है।

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

की समझ में क्यों हम तीन गुण आवश्यकता है जैसे , लिखने योग्य गणनीय और विन्यास। लिखने योग्य जावास्क्रिप्ट की बहुत कष्टप्रद भाग में से एक है, अगर आप अगर फिर से इस चलाने के लिए, सब कुछ करते हैं के गलत पर लिखने योग्य बदलने टूट जाता है हो जाता है उदाहरण enter image description here

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

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

enter image description here

यदि आप गलत पर गणनीय निर्धारित करते हैं, आप अन्य सभी से संपत्ति toString छुपा सकते हैं। इस चलाते हैं फिर आप मिल जाएगा firstName, lastName विन्यास

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

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)); 

enter image description here

आप गलत पर विन्यास सेट करके इस व्यवहार को प्रतिबंधित कर सकते हैं।

Orginal reference of this information is from my personal Blog

+1

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

+0

@JacqueGoupil आप सही हैं। मैं स्क्रीन शॉट के बजाय कोड जोड़कर अपडेट करूंगा – JEMI

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