जावास्क्रिप्ट में एक कस्टम ऑब्जेक्ट "ठीक से" कैसे बनाएं?


471

मुझे आश्चर्य है कि एक जावास्क्रिप्ट ऑब्जेक्ट बनाने के लिए सबसे अच्छा तरीका क्या है जिसमें गुण और विधियां हैं।

मैंने ऐसे उदाहरण देखे हैं जहां व्यक्ति ने उपयोग किया var self = thisऔर फिर self.सभी कार्यों में उपयोग करके यह सुनिश्चित करने के लिए कि गुंजाइश हमेशा सही है।

तब मैंने .prototypeगुणों को जोड़ने के लिए उदाहरणों को देखा है , जबकि अन्य इसे इनलाइन करते हैं।

क्या कोई मुझे कुछ गुणों और विधियों के साथ जावास्क्रिप्ट ऑब्जेक्ट का उचित उदाहरण दे सकता है?


13
कोई "सर्वश्रेष्ठ" तरीका नहीं है।
ट्रिप्टिक

selfआरक्षित शब्द नहीं है? अगर नहीं, तो ऐसा होना चाहिए; चूंकि selfवर्तमान विंडो का जिक्र एक पूर्व-परिभाषित चर है। self === window
शज

2
@ शाज: यह windowब्राउजर ऑब्जेक्ट मॉडल जैसे documentया के अन्य गुणों से अधिक आरक्षित शब्द नहीं है frames; आप निश्चित रूप से पहचानकर्ता को एक चर नाम के रूप में फिर से उपयोग कर सकते हैं। हालांकि, हाँ, स्टाइलिस्टिकली मैं var that= thisकिसी भी संभावित भ्रम से बचना पसंद करता हूं । हालांकि window.selfअंतत: निरर्थक है, इसलिए इसे छूने का कोई कारण शायद ही हो।
बोबोंस

7
जब जेएस को छोटा किया जाता है, तो thisस्थानीय चर (जैसे self) को असाइन करना फ़ाइल आकार को कम करता है।
पैट्रिक फिशर

क्लासज न्यू लिंक: github.com/divio/classjs
निकोला

जवाबों:


889

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

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

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

function Shape(x, y) {
    this.x= x;
    this.y= y;
}

हम new Shapeउन्हें इस prototypeरचनाकार के लुकअप में लिखकर बनाए गए इंस्टेंस के तरीकों को जोड़ सकते हैं :

Shape.prototype.toString= function() {
    return 'Shape at '+this.x+', '+this.y;
};

अब इसे subclass करने के लिए, जितना आप कॉल कर सकते हैं उतना ही जावास्क्रिप्ट सबक्लासिंग करता है। हम उस अजीब जादू prototypeसंपत्ति को पूरी तरह से बदलकर ऐसा करते हैं:

function Circle(x, y, r) {
    Shape.call(this, x, y); // invoke the base class's constructor function to take co-ords
    this.r= r;
}
Circle.prototype= new Shape();

इसमें विधियाँ जोड़ने से पहले:

Circle.prototype.toString= function() {
    return 'Circular '+Shape.prototype.toString.call(this)+' with radius '+this.r;
}

यह उदाहरण काम करेगा और आपको कई ट्यूटोरियल में कोड दिखाई देगा। लेकिन आदमी, वह new Shape()बदसूरत है: हम आधार वर्ग को तात्कालिक कर रहे हैं, भले ही कोई वास्तविक आकार नहीं बनना है। यह इस सरल मामले में काम करने के लिए होता है क्योंकि जावास्क्रिप्ट इतना खराब है: यह अनुमति देता है शून्य तर्क, में पारित करने के लिए किया जा जिस स्थिति में xऔर yबन undefinedऔर प्रोटोटाइप के लिए आवंटित कर रहे हैं this.xऔर this.y। यदि कंस्ट्रक्टर फ़ंक्शन कुछ अधिक जटिल कर रहा था, तो यह उसके चेहरे पर फ्लैट हो जाएगा।

तो हमें जो करने की ज़रूरत है वह एक प्रोटोटाइप ऑब्जेक्ट बनाने का एक तरीका है जिसमें बेस क्लास के कंस्ट्रक्टर फ़ंक्शन को कॉल किए बिना तरीकों और अन्य सदस्यों को हम एक वर्ग स्तर पर चाहते हैं। ऐसा करने के लिए हमें हेल्पर कोड लिखना शुरू करना होगा। यह सबसे सरल तरीका है जो मुझे पता है:

function subclassOf(base) {
    _subclassOf.prototype= base.prototype;
    return new _subclassOf();
}
function _subclassOf() {};

यह बेस क्लास के सदस्यों को अपने प्रोटोटाइप में एक नए कंस्ट्रक्टर फंक्शन में ट्रांसफर करता है, जो कुछ नहीं करता है, फिर उस कंस्ट्रक्टर का इस्तेमाल करता है। अब हम बस लिख सकते हैं:

function Circle(x, y, r) {
    Shape.call(this, x, y);
    this.r= r;
}
Circle.prototype= subclassOf(Shape);

new Shape()गलत के बजाय । अब हमारे पास निर्मित कक्षाओं के लिए प्राथमिकताओं का एक स्वीकार्य सेट है।

इस मॉडल के अंतर्गत हम कुछ परिशोधन और विस्तार पर विचार कर सकते हैं। उदाहरण के लिए यहां एक वाक्य रचना-चीनी संस्करण है:

Function.prototype.subclass= function(base) {
    var c= Function.prototype.subclass.nonconstructor;
    c.prototype= base.prototype;
    this.prototype= new c();
};
Function.prototype.subclass.nonconstructor= function() {};

...

function Circle(x, y, r) {
    Shape.call(this, x, y);
    this.r= r;
}
Circle.subclass(Shape);

किसी भी संस्करण में दोष यह है कि कंस्ट्रक्टर फ़ंक्शन को विरासत में प्राप्त नहीं किया जा सकता है, क्योंकि यह कई भाषाओं में है। इसलिए, भले ही आपका उपवर्ग निर्माण प्रक्रिया में कुछ भी नहीं जोड़ता है, यह आधार निर्माता को उन तर्कों के साथ याद करना होगा जो आधार चाहते थे। इसका उपयोग करके इसे थोड़ा स्वचालित किया जा सकता है apply, लेकिन फिर भी आपको लिखना होगा:

function Point() {
    Shape.apply(this, arguments);
}
Point.subclass(Shape);

इसलिए एक सामान्य विस्तार यह है कि निर्माणकर्ता के बजाय अपने स्वयं के कार्य में आरंभीकरण सामग्री को बाहर निकालना। यह फ़ंक्शन तब आधार से ठीक वारिस हो सकता है:

function Shape() { this._init.apply(this, arguments); }
Shape.prototype._init= function(x, y) {
    this.x= x;
    this.y= y;
};

function Point() { this._init.apply(this, arguments); }
Point.subclass(Shape);
// no need to write new initialiser for Point!

अब हमें प्रत्येक वर्ग के लिए एक ही निर्माता फ़ंक्शन बॉयलरप्लेट मिल गया है। हो सकता है कि हम इसे अपने स्वयं के सहायक कार्य में स्थानांतरित कर सकते हैं, इसलिए हमें इसे टाइप करने की आवश्यकता नहीं है, उदाहरण के लिए Function.prototype.subclass, इसे गोल करने और बेस क्लास के फ़ंक्शन को उपवर्गों को बाहर करने दें:

Function.prototype.makeSubclass= function() {
    function Class() {
        if ('_init' in this)
            this._init.apply(this, arguments);
    }
    Function.prototype.makeSubclass.nonconstructor.prototype= this.prototype;
    Class.prototype= new Function.prototype.makeSubclass.nonconstructor();
    return Class;
};
Function.prototype.makeSubclass.nonconstructor= function() {};

...

Shape= Object.makeSubclass();
Shape.prototype._init= function(x, y) {
    this.x= x;
    this.y= y;
};

Point= Shape.makeSubclass();

Circle= Shape.makeSubclass();
Circle.prototype._init= function(x, y, r) {
    Shape.prototype._init.call(this, x, y);
    this.r= r;
};

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

Function.prototype.makeSubclass= function() {
    function Class() {
        if (!(this instanceof Class))
            throw('Constructor called without "new"');
        ...

हो सकता है कि आप सभी नए सदस्यों को पारित करना चाहते हैं और makeSubclassउन्हें प्रोटोटाइप में जोड़ दिया है, Class.prototype...जिससे आपको बहुत कुछ लिखना है। बहुत सारे क्लास सिस्टम ऐसा करते हैं, जैसे:

Circle= Shape.makeSubclass({
    _init: function(x, y, z) {
        Shape.prototype._init.call(this, x, y);
        this.r= r;
    },
    ...
});

कई संभावित विशेषताएं हैं जिन्हें आप ऑब्जेक्ट सिस्टम में वांछनीय मान सकते हैं और कोई भी वास्तव में एक विशेष सूत्र पर सहमत नहीं होता है।


बंद रास्ता है, तो। यह जावास्क्रिप्ट की प्रोटोटाइप-आधारित विरासत की समस्याओं से बचा जाता है, विरासत का उपयोग न करके। बजाय:

function Shape(x, y) {
    var that= this;

    this.x= x;
    this.y= y;

    this.toString= function() {
        return 'Shape at '+that.x+', '+that.y;
    };
}

function Circle(x, y, r) {
    var that= this;

    Shape.call(this, x, y);
    this.r= r;

    var _baseToString= this.toString;
    this.toString= function() {
        return 'Circular '+_baseToString(that)+' with radius '+that.r;
    };
};

var mycircle= new Circle();

अब हर एक उदाहरण के Shapeपास toStringविधि की अपनी प्रति होगी (और कोई अन्य विधि या अन्य वर्ग के सदस्य जो हम जोड़ते हैं)।

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

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

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

var ts= mycircle.toString;
alert(ts());

तब thisविधि के अंदर अपेक्षा के अनुसार सर्किल उदाहरण नहीं होगा (यह वास्तव में वैश्विक windowऑब्जेक्ट होगा, जिससे व्यापक डिबगिंग शोक होगा)। वास्तव में यह आमतौर पर तब होता है जब एक विधि को लिया जाता है और उसे सौंपा जाता है setTimeout, onclickया EventListenerसामान्य रूप से।

प्रोटोटाइप तरीके के साथ, आपको हर ऐसे असाइनमेंट के लिए एक क्लोजर शामिल करना होगा:

setTimeout(function() {
    mycircle.move(1, 1);
}, 1000);

या, भविष्य में (या अब यदि आप Function.prototype को हैक करते हैं) तो आप इसके साथ भी कर सकते हैं function.bind():

setTimeout(mycircle.move.bind(mycircle, 1, 1), 1000);

यदि आपके उदाहरणों को बंद करने के तरीके से किया जाता है, तो बंधन को उदाहरण चर (आमतौर पर कहा जाता है thatया selfहालांकि, व्यक्तिगत रूप से मैं बाद में selfपहले से ही जावास्क्रिप्ट में अलग अर्थ है) के खिलाफ सलाह देगा, द्वारा बंद करने के लिए मुफ्त में किया जाता है । 1, 1हालांकि, उपरोक्त स्निपेट में आपको तर्क-वितर्क मुफ्त में नहीं मिलते हैं , इसलिए आपको अभी भी एक और बंद करने की आवश्यकता होगी या bind()यदि आपको ऐसा करने की आवश्यकता है।

क्लोजर विधि पर भी बहुत सारे वेरिएंट हैं। आप thisपूरी तरह से चूकना पसंद कर सकते हैं , एक नया बनाने thatऔर newऑपरेटर का उपयोग करने के बजाय इसे वापस कर सकते हैं :

function Shape(x, y) {
    var that= {};

    that.x= x;
    that.y= y;

    that.toString= function() {
        return 'Shape at '+that.x+', '+that.y;
    };

    return that;
}

function Circle(x, y, r) {
    var that= Shape(x, y);

    that.r= r;

    var _baseToString= that.toString;
    that.toString= function() {
        return 'Circular '+_baseToString(that)+' with radius '+r;
    };

    return that;
};

var mycircle= Circle(); // you can include `new` if you want but it won't do anything

कौन सा तरीका "उचित" है? दोनों। कौन सा "सर्वश्रेष्ठ" है? जो आपकी स्थिति पर निर्भर करता है। FWIW मैं वास्तविक जावास्क्रिप्ट वंशानुक्रम के लिए प्रोटोटाइप की ओर जाता हूं जब मैं दृढ़ता से OO सामान कर रहा हूं, और सरल फेंकने वाले पृष्ठ प्रभावों के लिए बंद हो जाता हूं।

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

[यह जावास्क्रिप्ट क्यों मेरी पसंदीदा प्रोग्रामिंग भाषा नहीं है का हिस्सा 94 है।]


13
बहुत अच्छा क्रमिक कदम के माध्यम से "वर्ग" वस्तु तात्कालिकता को हराया। और बायपास पर अच्छा स्पर्श new
वर्धमान ताजा

8
ऐसा लगता है जैसे जावास्क्रिप्ट आपकी पसंदीदा भाषा नहीं है क्योंकि आप इसका उपयोग करना चाहते हैं जैसे कि इसमें कक्षाएं थीं।
जोनाथन फीनबर्ग

59
निश्चित रूप से मैं ऐसा करता हूं, हर कोई करता है: क्लास-एंड-इंस्टेंस मॉडल आज की अधिकांश सामान्य समस्याओं के लिए अधिक प्राकृतिक है जो प्रोग्रामर सामना करते हैं। मैं इस बात से सहमत हूं कि, सिद्धांत के आधार पर, प्रोटोटाइप-आधारित वंशानुक्रम संभावित रूप से अधिक लचीले तरीके से काम कर सकता है, लेकिन जावास्क्रिप्ट पूरी तरह से उस वादे को पूरा नहीं करता है। इसका क्लंकी कंस्ट्रक्टर फंक्शन सिस्टम हमें दोनों दुनियाओं में से सबसे खराब देता है, जिससे कक्षा जैसी विरासत को कठिन बनाया जा सकता है, बशर्ते कोई भी लचीलापन या सादगी वाला प्रोटोटाइप पेश न कर सके। संक्षेप में, यह पू है।
bobince

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

4
यह हमेशा मुझे प्रतीत होता है कि जावास्क्रिप्ट जैसी प्रोटोटाइपिक भाषाओं पर शास्त्रीय वंशानुक्रम प्रतिमानों को रेखांकन एक वर्ग खूंटी और एक गोल छेद है। क्या कई बार यह वास्तव में आवश्यक है या यह सिर्फ लोगों के लिए भाषा को मुट्ठी में बांधने का एक तरीका है जिस तरह से वे चाहते हैं कि वह भाषा का उपयोग करने के बजाय केवल यह चाहते हैं कि यह क्या है?
slf

90

मैं इस पैटर्न का अक्सर उपयोग करता हूं - मैंने पाया है कि जब मुझे इसकी आवश्यकता होती है तो यह मुझे बहुत अधिक मात्रा में लचीलापन देता है। उपयोग में यह जावा-शैली की कक्षाओं के समान है।

var Foo = function()
{

    var privateStaticMethod = function() {};
    var privateStaticVariable = "foo";

    var constructor = function Foo(foo, bar)
    {
        var privateMethod = function() {};
        this.publicMethod = function() {};
    };

    constructor.publicStaticMethod = function() {};

    return constructor;
}();

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

मूल रूप से, यह दृष्टिकोण एक अधिक शक्तिशाली वर्ग बनाने के लिए मानक जावास्क्रिप्ट वस्तुओं के साथ क्रॉफोर्डियन दृष्टिकोण को जोड़ता है।

आप इसे वैसे ही इस्तेमाल कर सकते हैं जैसे आप किसी अन्य जावास्क्रिप्ट ऑब्जेक्ट को:

Foo.publicStaticMethod(); //calling a static method
var test = new Foo();     //instantiation
test.publicMethod();      //calling a method

4
यह दिलचस्प लग रहा है, क्योंकि यह मेरे "घर-मैदान" के करीब है जो कि सी # है। मैं भी लगता है कि मैं समझने के लिए क्यों privateStaticVariable वास्तव में निजी है शुरू (के रूप में यह एक समारोह के दायरे के भीतर परिभाषित और लंबे समय के रूप में वहाँ यह करने के लिए संदर्भ के रूप में जीवित रखा है?)
माइकल Stum

चूंकि इसका उपयोग नहीं किया जा रहा thisहै, इसलिए इसे अभी भी तुरंत चालू करने की आवश्यकता है new?
जॉर्डन पार्कर

वास्तव में, this करता है में आदत हो constructorसमारोह है, जो हो जाता है Fooउदाहरण में।
ShZ

4
यहां समस्या यह है कि प्रत्येक वस्तु को सभी निजी और सार्वजनिक कार्यों की अपनी प्रति मिलती है।
virtualnobi

2
@virtualnobi: यह पैटर्न आपको प्रॉपर तरीके लिखने से नहीं रोकता है constructor.prototype.myMethod = function () { ... }:।
निकोलस ले थिएरी डी'नेक्विन

25

डगलस Crockford में बड़े पैमाने पर है कि विषय पर चर्चा अच्छा पार्ट्स । वह नए ऑब्जेक्ट बनाने के लिए नए ऑपरेटर से बचने की सलाह देता है । इसके बजाय वह अनुकूलित निर्माण करने का प्रस्ताव रखता है। उदाहरण के लिए:

var mammal = function (spec) {     
   var that = {}; 
   that.get_name = function (  ) { 
      return spec.name; 
   }; 
   that.says = function (  ) { 
      return spec.saying || ''; 
   }; 
   return that; 
}; 

var myMammal = mammal({name: 'Herb'});

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

function Person() {
   this.name = "John";
   return this;
}

var person = new Person();
alert("name: " + person.name);**

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


5
क्या यह मुझे या मुझे लगता है कि क्रॉकफोर्ड नए ऑपरेटर के अपने कोसने के साथ बिल्कुल कोई मतलब नहीं है?
मध्यपूर्व

3
@meder: सिर्फ आप ही नहीं। कम से कम, मुझे लगता है कि नए ऑपरेटर के साथ कुछ भी गलत नहीं है। और वैसे भी इसमें एक निहितार्थ newहै var that = {};
टिम डाउन

17
क्रॉकफोर्ड एक क्रैंकी बूढ़ा आदमी है और मैं उससे बहुत असहमत हूं, लेकिन वह कम से कम जावास्क्रिप्ट पर एक महत्वपूर्ण नज़र डालने को बढ़ावा दे रहा है और यह सुनने लायक है कि उसे क्या कहना है।
बॉबिन

2
@ बब्बन: सहमत। करीबियों पर उनके लेखन ने लगभग 5 साल पहले बहुत सारी चीजें खोलीं, और उन्होंने एक विचारशील दृष्टिकोण को प्रोत्साहित किया।
टिम डाउन

20
मैं क्रॉकफोर्ड से सहमत हूं। नए ऑपरेटर के साथ मुद्दा यह है कि जावास्क्रिप्ट "का" संदर्भ देगा जब किसी फ़ंक्शन को कॉल करने की तुलना में बहुत अलग। उचित केस कन्वेंशन के बावजूद, ऐसे मुद्दे हैं जो बड़े कोड बेस पर उत्पन्न होते हैं क्योंकि डेवलपर्स नए का उपयोग करना भूल जाते हैं, कैपिटलाइज़ करना भूल जाते हैं, आदि। व्यावहारिक होने के लिए, आप वह सब कुछ कर सकते हैं जो आपको नए कीवर्ड के बिना करने की आवश्यकता है - इसलिए इसका उपयोग क्यों करें कोड में विफलता के अधिक बिंदुओं का परिचय दें? JS एक प्रोटोटाइप है, वर्ग आधारित नहीं, भाषा है। तो हम यह क्यों चाहते हैं कि यह एक स्थिर टाइप की भाषा की तरह काम करे? मैं निश्चित रूप से नहीं।
जोशुआ रामिरेज़

13

बॉबिन के उत्तर को जारी रखने के लिए

Es6 में अब आप वास्तव में एक बना सकते हैं class

तो अब आप कर सकते हैं:

class Shape {
    constructor(x, y) {
        this.x = x;
        this.y = y;
    }

    toString() {
        return `Shape at ${this.x}, ${this.y}`;
    }
}

तो एक सर्कल तक विस्तार करें (दूसरे उत्तर में) आप कर सकते हैं:

class Circle extends Shape {
    constructor(x, y, r) {
        super(x, y);
        this.r = r;
    }

    toString() {
        let shapeString = super.toString();
        return `Circular ${shapeString} with radius ${this.r}`;
    }
}

Es6 में थोड़ा क्लीनर समाप्त होता है और पढ़ने में थोड़ा आसान होता है।


यहाँ कार्रवाई में इसका एक अच्छा उदाहरण है:


6

संरचनाओं का उपयोग करके आप इसे इस तरह भी कर सकते हैं:

function createCounter () {
    var count = 0;

    return {
        increaseBy: function(nb) {
            count += nb;
        },
        reset: function {
            count = 0;
        }
    }
}

फिर :

var counter1 = createCounter();
counter1.increaseBy(4);

6
मुझे वह तरीका पसंद नहीं है क्योंकि व्हॉट्सएप महत्वपूर्ण है। वापसी के बाद घुंघराले क्रॉस-ब्राउज़र संगतता के लिए एक ही लाइन पर होना चाहिए।
जियोवा 4

5

एक और तरीका होगा http://jsfiddle.net/nnUY4/ (मुझे नहीं पता कि इस तरह की वस्तु निर्माण और खुलासा कार्य किसी विशिष्ट पैटर्न का पालन करते हैं)

// Build-Reveal

var person={
create:function(_name){ // 'constructor'
                        //  prevents direct instantiation 
                        //  but no inheritance
    return (function() {

        var name=_name||"defaultname";  // private variable

        // [some private functions]

        function getName(){
            return name;
        }

        function setName(_name){
            name=_name;
        }

        return {    // revealed functions
            getName:getName,    
            setName:setName
        }
    })();
   }
  }

  // … no (instantiated) person so far …

  var p=person.create(); // name will be set to 'defaultname'
  p.setName("adam");        // and overwritten
  var p2=person.create("eva"); // or provide 'constructor parameters'
  alert(p.getName()+":"+p2.getName()); // alerts "adam:eva"

4

जब कोई एक निर्माता के आह्वान के दौरान "इस" को बंद करने की चाल का उपयोग करता है, तो यह एक फ़ंक्शन लिखने के लिए है जिसे किसी अन्य ऑब्जेक्ट द्वारा कॉलबैक के रूप में उपयोग किया जा सकता है जो किसी ऑब्जेक्ट पर एक विधि को लागू नहीं करना चाहता है। यह "स्कोप को सही बनाने" से संबंधित नहीं है।

यहाँ एक वेनिला जावास्क्रिप्ट वस्तु है:

function MyThing(aParam) {
    var myPrivateVariable = "squizzitch";

    this.someProperty = aParam;
    this.useMeAsACallback = function() {
        console.log("Look, I have access to " + myPrivateVariable + "!");
    }
}

// Every MyThing will get this method for free:
MyThing.prototype.someMethod = function() {
    console.log(this.someProperty);
};

आपको पढ़ने से बहुत कुछ मिल सकता है कि डगलस क्रॉकफोर्ड का जावास्क्रिप्ट के बारे में क्या कहना है। जॉन रेसिग भी शानदार हैं। सौभाग्य!


1
उह, चारों ओर से बंद thisकरने से "गुंजाइश सही बनाने" के साथ सब कुछ होता है।
रोतीन मार्थ

3
जोनाथन सही है। जेएस फ़ंक्शन का दायरा वह है जो आप इसे डिज़ाइन करते हैं। सेल्फ = यह ट्रिक इसे एक विशेष तरीके से बाँधने का एक तरीका है ताकि किसी अन्य संदर्भ में कॉल करने पर यह परिवर्तित न हो। लेकिन कभी-कभी यही आप वास्तव में चाहते हैं। संदर्भ पर निर्भर करता है।
मार्को

मुझे लगता है कि आप सभी वास्तव में एक ही बात कह रहे हैं। self=thisहालांकि thisज़बर्दस्ती करने के लिए मजबूर नहीं करता है, आसानी से एक बंद के माध्यम से "सही" स्कूपिंग की अनुमति देता है।
क्रीसेंट फ्रेश

2
कारण आप ऐसा करते हैं = यह नेस्टेड फ़ंक्शन को इस के दायरे तक पहुंच प्रदान करना है क्योंकि यह कंस्ट्रक्टर फ़ंक्शन में मौजूद है। जब नेस्टेड फ़ंक्शंस कंस्ट्रक्टर फ़ंक्शंस के अंदर होते हैं, तो उनका "यह" स्कोप ग्लोबल स्कोप में बदल जाता है।
जोशुआ रामिरेज़

4

Closureबहुमुखी है। बॉबिन ने वस्तुओं को बनाते समय प्रोटोटाइप बनाम क्लोजर दृष्टिकोण को अच्छी तरह से संक्षेप में प्रस्तुत किया है । हालाँकि आप OOPएक कार्यात्मक प्रोग्रामिंग तरीके से बंद करने के उपयोग के कुछ पहलुओं की नकल कर सकते हैं । याद रखें फ़ंक्शन जावास्क्रिप्ट में ऑब्जेक्ट हैं ; इसलिए एक अलग तरीके से ऑब्जेक्ट के रूप में फ़ंक्शन का उपयोग करें।

यहाँ बंद होने का एक उदाहरण है:

function outer(outerArg) {
    return inner(innerArg) {
        return innerArg + outerArg; //the scope chain is composed of innerArg and outerArg from the outer context 
    }
}

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

कैसे?

मान लीजिए आप कुछ वस्तुओं के वैट की गणना करना चाहते हैं। वैट एक आवेदन के जीवनकाल के दौरान स्थिर रहने की संभावना है। OOP (छद्म कोड) में इसे करने का एक तरीका:

public class Calculator {
    public property VAT { get; private set; }
    public Calculator(int vat) {
        this.VAT = vat;
    }
    public int Calculate(int price) {
        return price * this.VAT;
    }
}

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

function calculator(vat) {
    return function(item) {
        return item * vat;
    }
}
var calculate = calculator(1.10);
var jsBook = 100; //100$
calculate(jsBook); //110

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

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Closures


3

कोई वस्तु बनाना

जावास्क्रिप्ट में ऑब्जेक्ट बनाने का सबसे आसान तरीका निम्नलिखित सिंटैक्स का उपयोग करना है:

var test = {
  a : 5,
  b : 10,
  f : function(c) {
    return this.a + this.b + c;
  }
}

console.log(test);
console.log(test.f(3));

यह संरचित तरीके से डेटा संग्रहीत करने के लिए बहुत अच्छा काम करता है।

अधिक जटिल उपयोग के मामलों के लिए, हालांकि, अक्सर कार्यों के उदाहरणों को बनाना बेहतर होता है:

function Test(a, b) {
  this.a = a;
  this.b = b;
  this.f = function(c) {
return this.a + this.b + c;
  };
}

var test = new Test(5, 10);
console.log(test);
console.log(test.f(3));

यह आपको कई ऑब्जेक्ट्स बनाने की अनुमति देता है जो समान "खाका" साझा करते हैं, जैसे आप कक्षाओं में कैसे उपयोग करते हैं। जावा।

यह अभी भी अधिक कुशलता से किया जा सकता है, हालांकि, एक प्रोटोटाइप का उपयोग करके।

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

हमारे मामले में, यह विधि fको प्रोटोटाइप में स्थानांतरित करने के लिए समझ में आता है :

function Test(a, b) {
  this.a = a;
  this.b = b;
}

Test.prototype.f = function(c) {
  return this.a + this.b + c;
};

var test = new Test(5, 10);
console.log(test);
console.log(test.f(3));

विरासत

जावास्क्रिप्ट में वंशानुक्रम करने का एक सरल लेकिन प्रभावी तरीका, निम्नलिखित दो-लाइनर का उपयोग करना है:

B.prototype = Object.create(A.prototype);
B.prototype.constructor = B;

यह ऐसा करने के समान है:

B.prototype = new A();

दोनों के बीच मुख्य अंतर यह है कि निर्माणकर्ता का Aउपयोग करते समय नहीं चलाया जाता है Object.create, जो कि अधिक सहज और वर्ग आधारित विरासत के समान है।

आप हमेशा निर्माणकर्ता को वैकल्पिक रूप से चलाने के लिए चुन सकते हैं Aजब इसे बनाने वाले के साथ Bजोड़कर एक नया उदाहरण बनाया जाए B:

function B(arg1, arg2) {
    A(arg1, arg2); // This is optional
}

आप सभी तर्कों पास करना चाहते हैं Bके लिए A, आप भी उपयोग कर सकते हैं Function.prototype.apply():

function B() {
    A.apply(this, arguments); // This is optional
}

यदि आप किसी अन्य ऑब्जेक्ट को कंस्ट्रक्टर की श्रृंखला में मिलाना चाहते हैं B, तो आप इसके Object.createसाथ संयोजन कर सकते हैं Object.assign:

B.prototype = Object.assign(Object.create(A.prototype), mixin.prototype);
B.prototype.constructor = B;

डेमो

function A(name) {
  this.name = name;
}

A.prototype = Object.create(Object.prototype);
A.prototype.constructor = A;

function B() {
  A.apply(this, arguments);
  this.street = "Downing Street 10";
}

B.prototype = Object.create(A.prototype);
B.prototype.constructor = B;

function mixin() {

}

mixin.prototype = Object.create(Object.prototype);
mixin.prototype.constructor = mixin;

mixin.prototype.getProperties = function() {
  return {
    name: this.name,
    address: this.street,
    year: this.year
  };
};

function C() {
  B.apply(this, arguments);
  this.year = "2018"
}

C.prototype = Object.assign(Object.create(B.prototype), mixin.prototype);
C.prototype.constructor = C;

var instance = new C("Frank");
console.log(instance);
console.log(instance.getProperties());


ध्यान दें

Object.createIE9 + सहित प्रत्येक आधुनिक ब्राउज़र में सुरक्षित रूप से उपयोग किया जा सकता है। Object.assignIE के किसी भी संस्करण में काम नहीं करता है और न ही कुछ मोबाइल ब्राउज़र। इसे पॉलीफिल Object.create और / या करने की सिफारिश की जाती हैObject.assign यदि आप उनका उपयोग करना चाहते हैं और उन ब्राउज़रों का समर्थन करना चाहते हैं जो उन्हें लागू नहीं करते हैं।

आप के लिए एक polyfill पा सकते हैं Object.create यहाँ और के लिए एक Object.assign यहाँ


0
var Person = function (lastname, age, job){
this.name = name;
this.age = age;
this.job = job;
this.changeName = function(name){
this.lastname = name;
}
}
var myWorker = new Person('Adeola', 23, 'Web Developer');
myWorker.changeName('Timmy');

console.log("New Worker" + myWorker.lastname);

4
पहले से ही पेश किए गए कई व्यापक उत्तरों में क्या है?
1:00

मुझे यह उत्तर पसंद है क्योंकि यह संक्षिप्त है और कार्यान्वयन के तीन भागों को दर्शाता है: 1) वस्तु को परिभाषित करें, 2) वस्तु के एक उदाहरण को त्वरित करें, 3) उदाहरण का उपयोग करें - यह पार्स होने के बजाय एक नज़र में यह सब दिखाता है उपरोक्त सभी क्रियाओं के उत्तर के माध्यम से (जो, निश्चित रूप से, सभी प्रासंगिक विवरणों के साथ सभी बहुत अच्छे उत्तर हैं जो एक व्यक्ति चाहते हैं) - यहाँ एक साधारण सारांश की तरह
G-Man

0

2009 से स्वीकृत उत्तर के अलावा। यदि आप आधुनिक ब्राउज़रों को लक्षित कर सकते हैं, तो कोई भी Object.defineProperty का उपयोग कर सकता है ।

Object.defineProperty () विधि किसी ऑब्जेक्ट पर एक नई प्रॉपर्टी को सीधे परिभाषित करती है, या किसी ऑब्जेक्ट पर मौजूदा प्रॉपर्टी को संशोधित करती है, और ऑब्जेक्ट को लौटाती है। स्रोत: मोज़िला

var Foo = (function () {
    function Foo() {
        this._bar = false;
    }
    Object.defineProperty(Foo.prototype, "bar", {
        get: function () {
            return this._bar;
        },
        set: function (theBar) {
            this._bar = theBar;
        },
        enumerable: true,
        configurable: true
    });
    Foo.prototype.toTest = function () {
        alert("my value is " + this.bar);
    };
    return Foo;
}());

// test instance
var test = new Foo();
test.bar = true;
test.toTest();

डेस्कटॉप और मोबाइल संगतता सूची देखने के लिए, मोज़िला की ब्राउज़र संगतता सूची देखें । हां, IE9 + सफारी मोबाइल के साथ-साथ इसका समर्थन करता है।


0

आप भी इसे आजमा सकते हैं

    function Person(obj) {
    'use strict';
    if (typeof obj === "undefined") {
        this.name = "Bob";
        this.age = 32;
        this.company = "Facebook";
    } else {
        this.name = obj.name;
        this.age = obj.age;
        this.company = obj.company;
    }

}

Person.prototype.print = function () {
    'use strict';
    console.log("Name: " + this.name + " Age : " + this.age + " Company : " + this.company);
};

var p1 = new Person({name: "Alex", age: 23, company: "Google"});
p1.print();

0
एक पैटर्न जो मुझे अच्छी तरह से काम करता है
var Klass = function Klass() {
    var thus = this;
    var somePublicVariable = x
      , somePublicVariable2 = x
      ;
    var somePrivateVariable = x
      , somePrivateVariable2 = x
      ;

    var privateMethod = (function p() {...}).bind(this);

    function publicMethod() {...}

    // export precepts
    this.var1 = somePublicVariable;
    this.method = publicMethod;

    return this;
};

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

यहां बताया गया है कि मैं निर्णय लेता हूं कि कौन से घोषणा पत्र हैं:

  • संदर्भ ऑब्जेक्ट पर सीधे एक विधि घोषित न करें ( this)
  • चलो varघोषणाओं से प्राथमिकता दी जाती functionघोषणाओं
  • आदिम वस्तुओं ( {}और []) पर पूर्वता बरतने दें
  • चलो publicघोषणाओं से प्राथमिकता दी जातीprivateघोषणाओं
  • पसंद करते हैं Function.prototype.bindअधिक thus, self,vm ,etc
  • जब तक अन्य वर्ग के भीतर एक कक्षा घोषित करने से बचें:
    • यह स्पष्ट होना चाहिए कि दोनों अविभाज्य हैं
    • इनर क्लास कमांड पैटर्न को लागू करता है
    • इनर क्लास ने सिंगलटन पैटर्न को लागू किया
    • इनर वर्ग राज्य पैटर्न को लागू करता है
    • इनर क्लास एक और डिज़ाइन पैटर्न लागू करता है जो इसे वारंट करता है
  • क्लोजर स्पेस thisके लेक्सिकल स्कोप के भीतर से हमेशा लौटें ।

यहां देखें ये मदद क्यों:

कंस्ट्रक्टर हाइजैकिंग
var Super = function Super() {
    ...
    this.inherited = true;
    ...
};
var Klass = function Klass() {
    ...
    // export precepts
    Super.apply(this);  // extends this with property `inherited`
    ...
};
मॉडल डिजाइन
var Model = function Model(options) {
    var options = options || {};

    this.id = options.id || this.id || -1;
    this.string = options.string || this.string || "";
    // ...

    return this;
};
var model = new Model({...});
var updated = Model.call(model, { string: 'modified' });
(model === updated === true);  // > true
डिजाइन पैटर्न्स
var Singleton = new (function Singleton() {
    var INSTANCE = null;

    return function Klass() {
        ...
        // export precepts
        ...

        if (!INSTANCE) INSTANCE = this;
        return INSTANCE;
    };
})();
var a = new Singleton();
var b = new Singleton();
(a === b === true);  // > true

जैसा कि आप देख सकते हैं, मुझे वास्तव में कोई ज़रूरत नहीं है thusक्योंकि मैं पसंद करताFunction.prototype.bind.call.applythus हूं ( या ) । हमारी Singletonकक्षा में, हम इसका नाम भी नहीं देते thusक्योंकि INSTANCEअधिक जानकारी बताती है। इसके लिए Model, हम वापस लौटते हैं thisताकि हम कंस्ट्रक्टर .callको उस उदाहरण में वापस आने के लिए आमंत्रित कर सकें, जो हमने इसमें पारित किया था। अनावश्यक रूप से, हमने इसे चर को सौंपा updated, हालांकि यह अन्य परिदृश्यों में उपयोगी है।

साथ में, मैं का उपयोग कर वस्तु-शाब्दिक निर्माण पसंद करते हैं new {ब्रैकेट्स} पर कीवर्ड :

पसंदीदा
var klass = new (function Klass(Base) {
    ...
    // export precepts
    Base.apply(this);  //
    this.override = x;
    ...
})(Super);
पसंद नहीं किया गया
var klass = Super.apply({
    override: x
});

जैसा कि आप देख सकते हैं, बाद वाले के पास अपनी सुपरक्लास की "ओवरराइड" संपत्ति को ओवरराइड करने की कोई क्षमता नहीं है।

यदि मैं कक्षा की prototypeवस्तु में विधियाँ जोड़ता हूँ, तो मैं एक वस्तु शाब्दिक पसंद करता हूँ - newखोजशब्द का उपयोग किए बिना या उसके बिना :

पसंदीदा
Klass.prototype = new Super();
// OR
Klass.prototype = new (function Base() {
    ...
    // export precepts
    Base.apply(this);
    ...
})(Super);
// OR
Klass.prototype = Super.apply({...});
// OR
Klass.prototype = {
    method: function m() {...}
};
पसंद नहीं किया गया
Klass.prototype.method = function m() {...};

0

मैं यह उल्लेख करना चाहता हूं कि हम किसी वस्तु को घोषित करने के लिए शीर्षक या स्ट्रिंग का उपयोग कर सकते हैं ।
उनमें से प्रत्येक प्रकार को कॉल करने के अलग-अलग तरीके हैं। निचे देखो:

var test = {

  useTitle : "Here we use 'a Title' to declare an Object",
  'useString': "Here we use 'a String' to declare an Object",
  
  onTitle : function() {
    return this.useTitle;
  },
  
  onString : function(type) {
    return this[type];
  }
  
}

console.log(test.onTitle());
console.log(test.onString('useString'));


-1

मूल रूप से जेएस में कक्षा की कोई अवधारणा नहीं है इसलिए हम फ़ंक्शन का उपयोग एक क्लास कंस्ट्रक्टर के रूप में करते हैं जो मौजूदा डिज़ाइन पैटर्न के साथ प्रासंगिक है।

//Constructor Pattern
function Person(name, age, job){
 this.name = name;
 this.age = age;
 this.job = job;
 this.doSomething = function(){
    alert('I am Happy');
}
}

अब तक जेएस का कोई सुराग नहीं है कि आप एक ऑब्जेक्ट बनाना चाहते हैं इसलिए यहां नया कीवर्ड आता है।

var person1 = new Person('Arv', 30, 'Software');
person1.name //Arv

रेफरी: वेब डेवलपर्स के लिए पेशेवर जेएस - निक जेड


डाउनवोट स्वीकार किया गया: एक वैध कारण के साथ और अधिक जानकारीपूर्ण होता और सुधार का अवसर प्रदान करता।
Airwind711

वहाँ है एक की अवधारणा classजे एस में, जैसे आप अपने शीर्षक में उल्लेख किया है का उपयोग कर functionकीवर्ड। यह एक डिज़ाइन पैटर्न नहीं है, बल्कि भाषा का एक जानबूझकर विशेषता है। मैंने आपको इस पर ध्यान नहीं दिया, लेकिन ऐसा लगता है कि किसी और ने इस सवाल के बारे में अप्रासंगिकता और लगभग अप्रासंगिकता के कारण किया। आशा है कि यह प्रतिक्रिया मदद करती है।
कोडी
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.