2012-03-20 13 views
17

risposta embedded seguito in UPDATE 2/RISPOSTADeathmatch: self executing funzione anonima -confrontarli- "nuova funzione"

Grazie a Giuseppe per avermi aiutato a trovare la risposta (anche se non mi piace =) .

domanda iniziale

Pur facendo qualche ricerca sulle migliori pratiche quando si utilizza Namepsaces in JavaScript, mi sono imbattuto in questa definizione del "modello Modello": http://yuiblog.com/blog/2007/06/12/module-pattern/.

In genere uso questo schema da quando l'ho visto in YUI2 anni fa e questo articolo offre una buona panoramica del concetto. Ma ciò a cui non tocca è il motivo per cui "Auto Eseguendo funzioni anonime" vengono usate al posto di "nuova funzione". Questo è stato chiesto nei commenti, ma non è stato ben descritto dall'autore. Dato che questo articolo ha 4 o più anni (e non ho trovato la risposta altrove online) ho pensato di portarlo qui.

È già all'interno di una chiusura, quindi? (vedi: Why is this function wrapped in parentheses, followed by parentheses? che inoltre non risponde alla mia domanda =).

Supponendo che il seguente codice di impostazione ..

var MyNamespace = window.MyNamespace || {}; 

Quale di questi è preferito e perché?

MyNamespace.UsingNew = new function() { 
    var fnPrivate = function() { 
     return "secrets1"; 
    }; 

    this.property = "value1"; 
    this.method = function() { 
     return "property = " + this.property + ' ' + fnPrivate(); 
    } 
}; 

MyNamespace.UsingSelfEx = (function() { //# <- Added "pre-parens" suggested by chuckj 
    var fnPrivate = function() { 
     return "secrets2"; 
    }; 
    var fnReturn = {}; 

    fnReturn.property = "value2"; 
    fnReturn.method = function() { 
     return "property = " + this.property + ' ' + fnPrivate(); 
    } 

    return fnReturn; 
})(); 

UPDATE:

Sembra che, come jQuery, tutti i bambini freschi stanno usando "self executing funzioni anonime" (SEAF)! Ma proprio non capisco, perché trovo molto più pulito usare l'approccio .UsingNew mentre esponi le funzioni pubbliche alla definizione (piuttosto che al di sotto in un ritorno che deve essere mantenuto separatamente o essere costretto a usare l'oggetto inline notazione).

L'argomento per non aver bisogno "che = questo" non regge per una serie di ragioni:

  • Al fine di evitare "var = che questo" si finisce o fare una " var obj "più un'istruzione return (che deve gestire una definizione separata di ciò che è pubblico) o essere costretta a utilizzare la notazione dell'oggetto inline (return {1,2,3}) per le proprietà/i metodi pubblici.
  • Si può sempre creare una variabile privata di "var that = this" nella parte superiore della classe/spazio dei nomi e utilizzare "that" dappertutto.

Ora ... Suppongo che il mio stile di sviluppo possa rendere molto più facile la gestione del modello .UsingNew. Le mie funzioni "private" sono quasi sempre di natura "statica", quindi devo comunque passare nel contesto (supplendo alla necessità di "questo"). Ho anche preso l'abitudine di usare uno spazio dei nomi di "abbreviazione", quindi quando ho bisogno di accedere a "questo" mi riferisco semplicemente all'oggetto completo tramite lo spazio dei nomi "abbreviazione" piuttosto che tramite il suo percorso completo. Es .:

var PrivateFunct = function() { 
    var rThis = Cn._.val; //# The abbreviated form of Cn.Renderer.Form.Validation 
    //... 
}; 

o se si tratta di una funzione statica privato ...

var PrivateStaticFunct = function(oContext) { 
    //... 
}; 

Altri poi le ragioni sopra esposte, che personalmente trovo l'approccio .UsingNew molto più leggibile nella fonte.Ho una codebase piuttosto ampia che utilizza il pattern .UsingNew qui: http://code.google.com/p/cn-namespace/source/browse/Cn.Web/js/Cn/ con il Validation functionality probabilmente il modo più semplice per ottenere la vostra testa in 1 lettura veloce attraverso. Uso anche alcune funzioni SEAF (vedi ErrorMessages.js.aspx) ma solo quando hanno senso.

Non riesco a immaginare di dover mantenere resi separati per esporre le interfacce pubbliche in fondo! Che schifo!

Ora, non fraintendetemi, ci sono un certo numero di posti in cui un SEAF è molto utile al fine di far rispettare la chiusura, ma personalmente penso che sia abusato all'interno degli oggetti.

UPDATE 2:

Su ulteriore riflessione (e grazie a una discussione estesa con Giuseppe sotto la sua risposta) non sembra esserci alcune regole che possono essere applicati a questo DEATHMATCH:

Per Douglas Crockford (vedi: JS we hardly new Ya) Anonymous funzioni non dovrebbero mai utilizzare la "nuova" parola chiave perché:

  • E 'più veloce di utilizzare un oggetto l curartene.
  • Usando il nuovo per invocare la funzione, l'oggetto rimane su un oggetto prototipo senza valore. Ciò spreca memoria senza vantaggi offensive. Se non usiamo il nuovo, non conserviamo l'oggetto prototipo sprecato nella catena. (NOTA: le chiamate di prototipo arrivano dopo la definizione del costruttore, e come SEAF o "nuove" funzioni anonime vengono lanciate imminentemente non è possibile utilizzare prototipo con esse)
  • Richiede meno codice per utilizzare un oggetto letterale. (anche se è vero, non sono d'accordo perché odio la notazione letterale dell'oggetto, preferirei molto usarla; è piuttosto allora, è più leggibile).
  • Non è mai una buona idea mettere direttamente nuovo davanti alla funzione. Ad esempio, la nuova funzione non offre alcun vantaggio nella costruzione di nuovi oggetti.

Così sembra la carne e le patate della questione è questo: SEAF di sono preferibili per var obj = new function() {...}; a causa di velocità e meno overhead (nessun prototipo oggetto non necessari). Ciò che devi sopportare è il fatto che sei costretto a usare la notazione letterale dell'oggetto (quindi, piuttosto che i suoi membri pubblici) o mantenere un elenco separato di oggetti pubblici in un oggetto di ritorno.

SEAF non è consigliabile quando si intende che la funzione funzioni come costruttore di oggetti come instanceof non funzionerà come previsto (vedere: creating objects from JS closure: should i use the “new” keyword?).

RISPOSTA:

  • Se si tratta di una funzione anonima destinata a funzionare come un Singleton/Global statico instance, utilizzare SEAF.
  • Se si intende che funzioni come Constructor (per rappresentare potenzialmente più oggetti) o si utilizza .prototype, utilizzare una definizione di funzione "standard" e invocare con "nuovo", ad es.:

    function PseudoClass1() {}

    var PseudoClass2 = function() {};

    var myClass1 = new PseudoClass1();

    var myClass2 = new PseudoClass2();

devo dire, io non sono contento di questa risposta;) trovo il modello .UsingNew MOLTO più leggibile nel codebase, ma è più lento e utilizza più memoria di SEAF per il fatto che th Il riferimento del prototipo inutilizzato viene istanziato e lasciato nella catena degli oggetti.

+1

(in ritardo alla festa lo so ..) Un sacco di roba interessante ... dato che si preferisce con forza l'UsingNew I don Penso che il prototipo inutilizzato sia una ragione sufficiente per cambiare il tuo approccio ... è una quantità minima di memoria (per ragioni di comparazione, è molto più minimale della quantità che i programmatori che usano il modello di modulo sono disposti a sacrificare non usando prototipi.) –

+1

In termini di approccio alla funzione anonima, forse sembrerebbe più leggibile se si richiamasse la variabile di ritorno "self" invece di qualcosa come "fnReturn". Si potrebbe anche passare qualsiasi oggetto vuoto alla funzione e avere un "sé" come parametro; quindi assecondare la dichiarazione di ritorno e l'uso di "sé" invece di "questo", il corpo della funzione sarebbe identico al primo esempio. –

+0

Si dovrebbe [sicuramente non usare 'nuova funzione'] (http://stackoverflow.com/a/10406585/1048572) – Bergi

risposta

7

Per prima cosa, questo schema:

MyNamespace.UsingNew = new function() { 
    var fnPrivate = function() { 

     //what's this in here? 

     return "secrets1"; 
    }; 

    this.property = "value1"; 
    this.method = function() { 

     //what's this in here? 

     return "property = " + this.property + ' ' + fnPrivate(); 
    } 
}; 
  • utilizza la "nuova" parola chiave per creare un'istanza di un oggetto, che è modellato utilizzando una funzione di costruzione. dimenticando di usare "nuovo", finirai con il nome creando una funzione MyNamespace.UsingNew() invece di un oggetto.

  • è una funzione di costruzione e non ancora un'istanza di un oggetto (fino a quando non lo si costruisce utilizzando nuovo). devi usare "questo" per ottenere l'oggetto che diventerà. aggiunge solo problemi al campo di applicazione, soprattutto quando annidate più funzioni in esso, e il valore di "questo" cambierà di volta in volta (e non lo vedrete arrivare fino a quando la console non vi dirà). familiarità con self=this o that=this per salvare il valore di "questo"? è praticamente visto quando si utilizza questo modello

sul otherhand, il modello successivo è un po 'meglio (per me) in quanto:

  • non c'è bisogno di usare "nuovo" in quanto restituisce un oggetto.

  • non utilizzare "questo" poiché restituisce già un oggetto. non hai nemmeno bisogno di "questo".

anche, preferisco costruire l'altro modello in questo modo:

ns.myobj = (function(){ 

    //private 
    var _privateProp = ''; 
    var _privateMeth = function(){}; 

    //public 
    var publicProp = ''; 
    var publicMeth = function(){}; 

    //expose public 
    return { 
     prop:publicProp, 
     meth:publicMeth 
    }; 
}()); 
+0

Innanzitutto, grazie per un'ottima risposta! – Campbeln

+0

Perdona la mia ignoranza, ma anche se dimentichi la parola chiave "nuova" e restituisci una funzione piuttosto che un oggetto, qual è la differenza? Sto indovinando funzionalità non molto, ma prestazioni (ly) cattive? Per quanto riguarda il tuo pref. metodo, non lo trovi un problema. incubo con il ritorno in fondo? Ho sempre usato ".UtilizzareNuovo": [link] (http://code.google.com/p/cn-namespace/source/browse/Cn.Web/js/Cn/Renderer/Form/Form.js) e riguardo a that = this issue, dal momento che ho creato Namespace mi occupo completamente delle funzioni richieste. Trovo che questo sia meno doloroso di un maint. quel ritorno. – Campbeln

+0

dai un'occhiata a questa domanda, dove ho chiesto se quando utilizzare "new" http://stackoverflow.com/q/9304473/575527 – Joseph

2

Entrambi sono quasi identici e dovrebbero avere le stesse caratteristiche di prestazioni relativamente. È solo una questione di stile. Personalmente, preferisco il secondo, ma è scritto un po 'funky. Vorrei scrivere che,

MyNamespace.UsingSelfEx = (function() { 
    function fnPrivate() { 
     return "secrets2"; 
    } 
    return { 
     property: "value2"; 
     method: function() { 
      return "property = " + this.property + " " + fnPrivate(); 
     } 
    } 
})(); 

Non si ha realmente bisogno le parentesi intorno alla funzione ma le funzioni di self-executing normalmente loro e lascia il lettore sapere, all'inizio, che si tratta di una funzione di auto di esecuzione.

+0

Agh, hai ragione! Le funzioni Self Executing generalmente hanno gli "extra" parenti attorno a loro! Sono abbastanza sorpreso che questo sembra essere il metodo preferito per fare questa funzionalità però. Mentre uso l'altro metodo, di tanto in tanto ho bisogno dell'approccio "questo = quello", ma trovo che FAR sia meno doloroso del fare il ritorno dell'oggetto in una delle sue forme. In più, mentre costruisco in Namespace, di solito mi riferisco all'intero indirizzo Namespace della funzione.function che devo usare. Tendo a progettare le mie funzioni "private" come "statiche" in modo che nessun dubbio aiuti con l'approccio .UsingNew. – Campbeln

+0

Vedi: [link] (http://code.google.com/p/cn-namespace/source/browse/Cn.Web/js/Cn/Renderer/Form/Form.js). Oh, e uso anche un Namespace "abbreviazione" (rappresentato da "Cn ._. *" Nel codice) per aiutare con le assegnazioni "rThis = Cn ._. Short" (piuttosto che "rThis = Cn.Some.Long .Namespace.Path "). – Campbeln

+0

Poiché questo sta creando uno spazio dei nomi e verrà eseguito solo quando questa è chiaramente solo una scelta di stile. Scegli quello che preferisci. Preferisco quanto sopra ma questa è solo un'opinione di ragazzi. – chuckj

0

Ho visto questo e mi è piaciuto

var MyThing = (function(){ 

    function MyThing(args){ 
    } 

    MyThing.prototype = { 
     prototypeMethod: function() {} 
    }; 

    return MyThing; 
})(); 

Si può anche fare questo

MyThing.create = function(args){ 
    return new MyThing(args); 
} 

Di solito è racchiuso in un altro sé invocare la funzione. Al fine di evitare conflitti di denominazione.

(function({ 

})(window); 

E l'oggetto window viene passato come argomento, per assegnare a un ambito superiore.

window.MyThing = MyThing; 

In questo esempio è possibile utilizzare, variabili statiche, privati ​​ecc

Problemi correlati