स्व-निष्पादन अनाम फ़ंक्शन बनाम प्रोटोटाइप


26

जावास्क्रिप्ट में कक्षा / नामस्थान बनाने और प्रबंधित करने के लिए कुछ स्पष्ट रूप से प्रमुख तकनीकें हैं।

मैं उत्सुक हूं कि एक तकनीक बनाम दूसरे का उपयोग करके कौन सी परिस्थितियां वारंट करती हैं। मैं एक को चुनना चाहता हूं और इसके साथ आगे बढ़ना चाहता हूं।

मैं उद्यम कोड लिखता हूं जिसे कई टीमों में बनाए रखा जाता है और साझा किया जाता है, और मैं यह जानना चाहता हूं कि बनाए रखने योग्य जावास्क्रिप्ट लिखते समय सबसे अच्छा अभ्यास क्या है?

मैं सेल्फ-एक्ज़ीक्यूटिंग बेनामी फ़ंक्शंस को प्राथमिकता देता हूं, लेकिन मैं उत्सुक हूं कि इन तकनीकों पर समुदाय का वोट क्या है।

प्रोटोटाइप:

function obj()
{
}

obj.prototype.test = function() { alert('Hello?'); };
var obj2 = new obj();
obj2.test();

सेल्फ-क्लोजिंग बेनामी फंक्शन:

//Self-Executing Anonymous Function 
(function( skillet, $, undefined ) {
    //Private Property
    var isHot = true;

    //Public Property
    skillet.ingredient = "Bacon Strips";

    //Public Method
    skillet.fry = function() {
        var oliveOil;

        addItem( "\t\n Butter \n\t" );
        addItem( oliveOil );
        console.log( "Frying " + skillet.ingredient );
    };

    //Private Method
    function addItem( item ) {
        if ( item !== undefined ) {
            console.log( "Adding " + $.trim(item) );
        }
    }     
}( window.skillet = window.skillet || {}, jQuery ));   
//Public Properties      
console.log( skillet.ingredient ); //Bacon Strips  

//Public Methods 
skillet.fry(); //Adding Butter & Fraying Bacon Strips 

//Adding a Public Property 
skillet.quantity = "12"; console.log( skillet.quantity ); //12   

//Adding New Functionality to the Skillet 
(function( skillet, $, undefined ) {
    //Private Property
    var amountOfGrease = "1 Cup";

    //Public Method
    skillet.toString = function() {
        console.log( skillet.quantity + " " + 
                     skillet.ingredient + " & " + 
                     amountOfGrease + " of Grease" );
        console.log( isHot ? "Hot" : "Cold" );
     };     

}( window.skillet = window.skillet || {}, jQuery ));
//end of skillet definition


try {
    //12 Bacon Strips & 1 Cup of Grease
    skillet.toString(); //Throws Exception 
} catch( e ) {
    console.log( e.message ); //isHot is not defined
}

मुझे लगता है कि मुझे यह उल्लेख करना चाहिए कि सेल्फ-एक्ज़ीक्यूटिंग बेनामी फ़ंक्शन jQuery टीम द्वारा उपयोग किया जाने वाला पैटर्न है।

अद्यतन जब मैंने यह प्रश्न पूछा तो मुझे वास्तव में वह महत्व नहीं दिखाई दिया जो मैं समझने की कोशिश कर रहा था। हाथ में असली मुद्दा यह है कि अपनी वस्तुओं के उदाहरण बनाने के लिए या ऐसे पैटर्न का उपयोग करने के लिए नए का उपयोग करें या नहीं, जिन्हें निर्माणकर्ताओं / newकीवर्ड के उपयोग की आवश्यकता नहीं है ।

मैंने अपना स्वयं का उत्तर जोड़ा, क्योंकि मेरी राय में हमें उन पैटर्नों का उपयोग करना चाहिए जो newकीवर्ड का उपयोग नहीं करते हैं ।

अधिक जानकारी के लिए कृपया मेरा उत्तर देखें।


1
क्या आप उन दो तकनीकों के संक्षिप्त उदाहरण दे सकते हैं जिनका आप वर्णन कर रहे हैं?
हे

मेरे सरल नमूने के कारण प्रोटोटाइप को कम मत समझना।
3

1
यह अपने आप
अरे

2
मैं अभिव्यक्ति को बंद करने या इसे कॉल करने के लिए किसी भी कोष्ठक नहीं देखता ...
अरे

1
(+1) कई डेवलपर्स के लिए नामस्थान की अनदेखी की गई है।
umlcat

जवाबों:


22

बाहरी घटनाओं (यानी window.onload) में हुक किए बिना स्क्रिप्ट के निष्पादन को स्वचालित करने के लिए स्व निष्पादित निष्पादन कार्यों का उपयोग किया जाता है।

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

दूसरी ओर, एक ऑब्जेक्ट प्रोटोटाइप को संशोधित करके, वंशानुक्रम को स्थापित करने के लिए उपयोग किया जाता है (या मूल निवासी का विस्तार करें)। इस पद्धति का उपयोग 1: n ऑब्जेक्ट को सामान्य विधियों या गुणों के साथ करने के लिए किया जाता है।

जब आप अलग-अलग कार्य करते हैं, तो आपको दूसरे के लिए प्राथमिकता में एक पैटर्न नहीं चुनना चाहिए । नेमस्पेसिंग के संदर्भ में, सेल्फ एक्सक्यूजिंग फंक्शन एक उपयुक्त विकल्प है।


7
ध्यान दें कि "सेल्फ एक्ज़ीक्यूटिंग अनाम फ़ंक्शंस" को आमतौर पर तुरंत-फंक्स्ड फंक्शन एक्सप्रेशन (IIFE) के रूप में जाना जाता है ।
वॉइथोस

मैं उनसे बचता हूं और सच कहूं तो मुझे IIFEs से प्यार नहीं मिला। वे एक्लिप्स में कोड डिबग और ब्रेक आउटलाइन के लिए एक गड़बड़ हैं। यदि आपको नामस्थान की आवश्यकता है तो उसे किसी वस्तु पर चिपका दें, यदि आपको इसे निष्पादित करने की आवश्यकता है तो इसे कॉल करें, और इनकैप्सुलेशन वास्तव में मुझे कुछ भी नहीं देता है।
डैनियल सोकोलोस्की

4

यहाँ वह पैटर्न है जिसका मैंने अभी उपयोग करना शुरू किया है (कल तक इसके बदलाव का उपयोग कर रहा हूँ):

function MyClass() {
    // attributes
    var privateVar = null;

    // function implementations
    function myPublicFunction() {
    }

    function myPrivateFunction() {
    }

    // public declarations
    this.myPublicFunction = myPublicFunction;
}

MyClass.prototype = new ParentClass(); // if required

इस पर कुछ विचार:

  1. आपको (anonymous)अपने डीबगर स्टैक के निशान में सब कुछ नाम (कोई अनाम फ़ंक्शन नहीं) के रूप में नहीं मिलना चाहिए ।
  2. यह सबसे साफ पैटर्न है जिसे मैंने अभी तक देखा है
  3. आप अपने क्रियान्वयन एपीआई को घोषणा के बिना युग्मित किए बिना आसानी से अपने समूह को जोड़ सकते हैं (मतलब कोई व्यक्ति आपके सार्वजनिक वर्ग के इंटरफ़ेस को बिना स्क्रॉल किए आसानी से दबा सकता है)

एकमात्र समय जो मैं prototypeअब उपयोग करता हूं वह वास्तव में विरासत को परिभाषित करना है।


5
इसके साथ कुछ समस्याएं हैं। निर्माणकर्ता के प्रत्येक आह्वान के साथ प्रत्येक "विधि" के लिए एक नया फ़ंक्शन ऑब्जेक्ट बनाया जाता है। इसके अलावा, एक निर्माणकर्ता को विरासत के लिए प्रोटोटाइप ऑब्जेक्ट की एक प्रति प्राप्त करने के लिए कॉल किया जाता है। Object.create (ParentClass.prototyp) का उपयोग करें, या Object.create के लिए एक शिम जैसेfunction clone(obj){return this typeof 'clone' ? this : new clone(clone.prototype=obj)}
अरे

@GGG: हाँ, आप अपने पहले बिंदु के साथ सही हैं। मैं कहता हूं (और मुझे अपनी पोस्ट में उल्लेख करना चाहिए) कि कार्यान्वयन के प्रत्येक विशिष्ट उपयोग के मामले के माध्यम से सोचा जाना चाहिए। prototypeजब तक आप सुझाव देते हैं, विधि के साथ मेरा मुद्दा है (जब तक कि कोई ऐसी विधि नहीं है जिससे मैं अपरिचित हूं, जो मामला हो सकता है) कि आप विशेषताओं को एनकैप्सुलेट करने की क्षमता खो देते हैं, जिसका अर्थ है सार्वजनिक रूप से सब कुछ खुला (जो दुनिया का अंत नहीं है , बस एक व्यक्तिगत प्राथमिकता)।
डेमियन ब्रेख्त

इसके अलावा, jsperf ( jsperf.com/object-create-vs-constructor-vs-object-literal/12 ) पर किए गए निम्न कार्य को देखने के बाद , मैं लगभग किसी भी दिन (फिर से) अतिरिक्त मेमोरी की लागत पर प्रदर्शन में वृद्धि करूँगा । विशिष्ट उपयोग के मामले के लिए बहुत व्यक्तिपरक)।
डेमियन ब्रेख्त

अब, यह सब कहते हुए, मैं केवल ECMA-262 के माध्यम से भाग रहा हूं, इसलिए सामान का एक गुच्छा हो सकता है जो मैं नहीं देख रहा हूं .. साथ ही, मैं क्रॉकफोर्ड के शब्द को सुसमाचार के रूप में नहीं लेता हूं .. हाँ, वह एक है क्षेत्र के विशेषज्ञों में से एक (बेशक सबसे महत्वपूर्ण में से एक), लेकिन यह हमेशा उसे हर समय 100% सही नहीं बनाता है। वहाँ अन्य विशेषज्ञ हैं (मुझे उनमें से एक नहीं;)) जो सम्मोहक तर्क के साथ विरोधाभासी राय रखते हैं।
डेमियन ब्रेख्त

2
आप इस चीज में दिलचस्पी ले सकते हैं, जिस पर मैं काम कर रहा हूं
हे

3

मैं प्रोटोटाइप का उपयोग करता हूं क्योंकि वे क्लीनर हैं और मानक वंशानुक्रम पैटर्न का पालन करते हैं। ब्राउज़र डेवलपमेंट या ऐसी स्थिति के लिए सेल्फ-इनवोकिंग फ़ंक्शंस बहुत बढ़िया होते हैं, जहां आपको पता नहीं होता है कि कोड कहाँ निष्पादित किया जा रहा है, लेकिन अन्यथा यह सिर्फ शोर है।

उदाहरण:

var me;

function MyObject () {
    this.name = "Something";
}

MyObject.prototype.speak = function speak () {
    return "Hello, my name is " + this.name;
};

me = new MyObject();
me.name = "Joshua";
alert(me.speak());

1
सेल्फ एक्ज़ीक्यूटिंग अनाम फ़ंक्शंस आपके लिए निजी फ़ंक्शंस को एक कक्षा तक पहुंचाने की अनुमति देने के लिए बहुत उपयोगी हैं।
ज़ी

1

मैं स्व-निष्पादन समारोह के साथ जाना चाहता हूं, लेकिन थोड़े अंतर के साथ:

MyClass = (function() {
     var methodOne = function () {};
     var methodTwo = function () {};
     var privateProperty = "private";
     var publicProperty = "public";

     return function MyClass() {
         this.methodOne = methodOne;
         this.methodTwo = methodTwo;
         this.publicProperty = publicProperty;
     };
})();

यदि यह दृष्टिकोण ज्यादा साफ-सुथरा लगता है, क्योंकि मैं लौटे हुए वैश्विक वैरिएबल को किसी इनपुट पैरामीटर (जैसे jQuery) से अलग करता हूं (आपने जिस तरह से लिखा है, वह शून्य को वापस करने के बराबर है और C # में एक रेफरी पैरामीटर का उपयोग करता है, जो मुझे थोड़ा सा मिल जाता है, या एक पॉइंटर को एक पॉइंटर में पास करना और सी ++ में इसे फिर से असाइन करना)। यदि मैं तब अतिरिक्त विधियों या गुणों को कक्षा में संलग्न करने जा रहा था, तो मैं प्रोटोटाइप इनहेरिटेंस (उदाहरण के लिए jQuery के $ .extend विधि के साथ) का उपयोग करूंगा, लेकिन यह अपने स्वयं के विस्तार () को रोल करने के लिए पर्याप्त आसान है:

var additionalClassMethods = (function () {
    var additionalMethod = function () { alert('Test Method'); };
    return { additionalMethod: additionalMethod };
})();

$.extend(MyClass.prototype, additionalClassMethods);

var m = new MyClass();
m.additionalMethod(); // Pops out "Test Method"

इस तरह आप जोड़े गए तरीकों और मूल लोगों के बीच एक स्पष्ट अंतर है।


1
क्या मैं केवल एक ऐसा व्यक्ति हूं जो NFE का इस तरह से उपयोग करने के बारे में सोचता है?
हे

1

लाइव उदाहरण

(function _anonymouswrapper(undefined) {

    var Skillet = {
        constructor: function (options) {
            options && extend(this, options);
            return this; 
        },
        ingredient: "Bacon Strips",
        _isHot: true,
        fry: function fry(oliveOil) {
            this._addItem("\t\n Butter \n\t");
            this._addItem(oliveOil);
            this._addItem(this.ingredient);
            console.log("Frying " + this.ingredient);
        },
        _addItem: function addItem(item) {
            console.log("Adding " + item.toString().trim());
        }
    };

    var skillet = Object.create(Skillet).constructor();

    console.log(skillet.ingredient);
    skillet.fry("olive oil");

    var PrintableSkillet = extend(Object.create(Skillet), {
        constructor: function constructor(options) {
            options && extend(this, options);
            return this;
        },
        _amountOfGrease: "1 Cup",
        quantity: 12,
        toString: function toString() {
            console.log(this.quantity + " " +
                        this.ingredient + " & " +
                        this._amountOfGrease + " of Grease");
            console.log(this._isHot ? "Hot" : "Cold");
        }
    });

    var skillet = Object.create(PrintableSkillet).constructor();

    skillet.toString();

    function extend(target, source) {
        Object.getOwnPropertyNames(source).forEach(function (name) {
            var pd = Object.getOwnPropertyDescriptor(source, name);
            Object.defineProperty(target, name, pd);
        });
        return target;
    }
}());

आप अपने कोड के आसपास "मॉड्यूल स्कोप" का अनुकरण करने के लिए एक IIFE का उपयोग कर सकते हैं। तब आप बस वस्तुओं का उपयोग कर सकते हैं जैसा कि आप सामान्य रूप से करते हैं।

क्लोजर का उपयोग करके निजी स्थिति का "अनुकरण" न करें क्योंकि इसमें एक बड़ा मेमोरी पेनल्टी है।

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


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

@EdWoodcock मुझे लगा कि कोड छोटी गाड़ी थी, बस इसे ठीक कर दिया गया और इसे ठीक कर दिया गया।
रयूनोस

@EdWoodcock स्थानीय consoleऔर वैश्विक के बीच दक्षता अंतर consoleएक सूक्ष्म अनुकूलन है। यह ऐसा करने के लायक है जिसे आप कम कर सकते हैं consoleलेकिन यह एक अलग मामला है
रेयनोस

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

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

0

अद्यतन मैं अब जावास्क्रिप्ट की एक बेहतर समझ है और लगता है कि मैं ठीक से प्रश्न का समाधान कर सकता हूं। मुझे लगता है कि यह एक खराब शब्द था, लेकिन पता करने के लिए बहुत महत्वपूर्ण जावास्क्रिप्ट विषय था।

सेल्फ एक्सेप्टिंग एनोनिमस फंक्शन पैटर्न वह नहीं है, जिसके लिए नए कीवर्ड के उपयोग की आवश्यकता होती है, यदि आप इस प्रकार के कार्यों से बाहर रहते हैं। मैं इस विचार से सहमत हूं कि नए का उपयोग करना एक पुरानी तकनीक है और हमें इसके बजाय ऐसे पैटर्न का उपयोग करने का प्रयास करना चाहिए जो नए के उपयोग से बचें।

सेल्फ एक्जिक्यूटिंग एनोनिमस फंक्शन इसी मापदंड को पूरा करता है।

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


1
नए कीवर्ड के बारे में क्या बुरा है? मैं उत्सुक हूँ।
सहयोगी

0

यहाँ है कि मैं इसके बारे IIFE SelfExecutingFunctionमें विस्तार के Myclassसाथ कैसे जाऊंगा Myclass.anotherFunction();

MyClass = (function() {
     var methodOne = function () {};
     var methodTwo = function () {};
     var privateProperty = "private";
     var publicProperty = "public";

    //Returning the anonymous object {} all the methods and properties are reflected in Myclass constructor that you want public those no included became hidden through closure; 
     return {
         methodOne = methodOne;
         methodTwo = methodTwo;
         publicProperty = publicProperty;
     };
})();

@@@@@@@@@@@@@@@@@@@@@@@@
//then define another IIFE SEF to add var function=anothermethod(){};
(function(obj){
return obj.anotherfunction=function(){console.log("Added to Myclass.anotherfunction");};
})(Myclass);

//the last bit : (function(obj){})(Myclass); obj === Myclass obj is an alias to pass the Myclass to IIFE

var myclass = new Myclass();
myclass.anotherfunction(); //"Added to Myclass.anotherfunction"

1
प्रोग्रामर दौरे है वैचारिक सवाल जवाब चीजों की व्याख्या करने की उम्मीद है। स्पष्टीकरण के बजाय कोड डंप फेंकना आईडीई से व्हाइटबोर्ड पर कोड कॉपी करने जैसा है: यह परिचित लग सकता है और यहां तक ​​कि कभी-कभी समझ में आता है, लेकिन यह अजीब लगता है ... बस अजीब है। व्हाइटबोर्ड में संकलक नहीं है
gnat
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.