Funzione anonimo autoesecuzione vs Prototipo

26

In Javascript ci sono alcune tecniche chiaramente prominenti per creare e gestire classi / spazi dei nomi in javascript.

Sono curioso di sapere quali situazioni giustificano l'utilizzo di una tecnica rispetto all'altra. Voglio sceglierne uno e seguirlo avanti.

Scrivo codice aziendale che viene gestito e condiviso tra più team e desidero sapere qual è la procedura migliore quando si scrive javascript gestibile?

Tendo a preferire le funzioni anonime che eseguono autonomamente, tuttavia sono curioso di sapere quale sia il voto della comunità su queste tecniche.

Prototype:

function obj()
{
}

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

Funzione anonima auto-chiusura:

//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
}

Ritengo che dovrei menzionare che la funzione anonima autoesecante è il modello utilizzato dal team jQuery.

Aggiorna Quando ho fatto questa domanda, non ho veramente visto l'importanza di ciò che stavo cercando di capire. Il vero problema è se utilizzare o meno nuove per creare istanze dei tuoi oggetti o per utilizzare pattern che non richiedono costruttori / uso della parola chiave new .

Ho aggiunto la mia risposta, perché a mio avviso dovremmo utilizzare modelli che non utilizzano la parola chiave new .

Per ulteriori informazioni, vedi la mia risposta.

    
posta Robotsushi 06.01.2012 - 04:38
fonte

7 risposte

0

Aggiorna Ora ho una migliore comprensione di javascript e sento di poter affrontare correttamente la domanda. Penso che si trattasse di un argomento javascript mal scritto, ma molto importante da affrontare.

Il pattern Self Executing Anonymous Function non è uno che richiede l'uso della nuova parola chiave se si evita l'uso di questo al di fuori delle funzioni. Sono d'accordo con l'idea che l'utilizzo di new sia una vecchia tecnica e dovremmo invece cercare di utilizzare modelli che evitino l'uso di nuovi.

La funzione anonima autoeseguibile soddisfa questi criteri.

La risposta a questa domanda è soggettiva, perché ci sono così tanti stili di codifica in javascript. Tuttavia, in base alle mie ricerche ed esperienze, consiglierei di scegliere di utilizzare la funzione anonima di autoesecuzione per definire le API e evitare l'uso di nuove quando possibile.

    
risposta data 07.07.2012 - 05:09
fonte
22

Le funzioni anonime autoesposte vengono utilizzate per automatizzare l'esecuzione dello script senza agganciarsi a eventi esterni (ad esempio window.onload).

In questo esempio viene utilizzato per formare il classico pattern Module, il cui scopo principale è introdurre uno spazio dei nomi nell'ambiente globale e fornire incapsulamento per qualsiasi proprietà interna che non sia "esportata" "o collegato allo spazio dei nomi.

La modifica di un prototipo di oggetti, d'altra parte, viene utilizzata per stabilire ereditarietà (o estendere i nativi). Questo modello è usato per produrre oggetti 1: n con metodi o proprietà comuni.

Non devi scegliere un modello in preferenza all'altro, poiché eseguono attività diverse . In termini di namespacing, la funzione di autoesecuzione è una scelta appropriata.

    
risposta data 06.01.2012 - 11:12
fonte
4

Ecco lo schema che ho appena iniziato a utilizzare (ne ho utilizzato le variazioni fino a ieri):

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

    // function implementations
    function myPublicFunction() {
    }

    function myPrivateFunction() {
    }

    // public declarations
    this.myPublicFunction = myPublicFunction;
}

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

Alcune riflessioni su questo:

  1. Non dovresti ottenere alcuna traccia di (anonymous) nelle tracce dello stack del debugger come ogni cosa è chiamata (nessuna funzione anonima).
  2. È lo schema più pulito che abbia mai visto
  3. Sei in grado di raggruppare facilmente le tue API esposte senza che le loro implementazioni siano accoppiate alla dichiarazione (il che significa che qualcuno può facilmente ingannare l'interfaccia della tua classe pubblica senza dover scorrere)

L'unica volta che userei più prototype è proprio per definire l'ereditarietà.

    
risposta data 06.01.2012 - 05:24
fonte
3

Uso prototipi perché sono più puliti e seguono schemi di ereditarietà standard. Le funzioni di autoinvocazione sono grandi per lo sviluppo del browser o una situazione in cui non si sa dove viene eseguito il codice, ma altrimenti è solo rumore.

Esempio:

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());
    
risposta data 06.01.2012 - 05:40
fonte
1

Vorrei andare con la funzione autoeseguibile, ma con una leggera differenza:

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

Se trovi questo approccio molto più pulito, come separo la variabile globale restituita da qualsiasi parametro di input (come jQuery) (il modo in cui lo hai scritto è equivalente a restituire void e usare un parametro ref in C #, che trovo un po ' off, o passare un puntatore a un puntatore e riassegnarlo in C ++). Se dovessi collegare metodi o proprietà aggiuntivi alla classe, utilizzerei l'ereditarietà prototipale (ad esempio con il metodo $ .extend di jQuery, ma è abbastanza semplice eseguire il rollover del proprio ext ()):

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"

In questo modo hai una chiara distinzione tra i metodi aggiunti e quelli originali.

    
risposta data 06.01.2012 - 12:15
fonte
1

Esempio dal vivo

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

Puoi usare un IIFE per emulare "scope del modulo" attorno al tuo codice. Quindi puoi semplicemente usare gli oggetti come al solito.

Non "emulare" lo stato privato usando chiusure in quanto ha una grande penalità di memoria.

Se stai scrivendo un'applicazione aziendale e vuoi mantenere l'utilizzo della memoria inferiore a 1 GB, evita di utilizzare inutilmente chiusure per memorizzare lo stato.

    
risposta data 06.01.2012 - 12:48
fonte
-1

Ecco come andrei su questo IIFE SelfExecutingFunction per estendere il Myclass con 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"
    
risposta data 23.01.2014 - 01:29
fonte

Leggi altre domande sui tag