Esecuzione JavaScript GenericsProblema che si desidera implementare "generici" in JavaScript. Teoria dei linguaggi di programmazione come Java e C # hanno un concetto di programmazione chiamato farmaci generici. L'idea alla base dei farmaci generici è quello di scrivere codice in modo tale che un identificatore è definito come un tipo generale che viene manipolato in una classe o un metodo. Ad esempio, il seguente codice sarebbe una classe generica in Java o C #: classe Container La classe Container è un parametro generico, tipo, che può fare riferimento a qualsiasi altro tipo. E nella dichiarazione dei container, il membro _managed dati è dello stesso tipo come parametro generico. In un concetto astratto, Container gestisce ogni tipo. Un programmatore JavaScript potrebbe guardare il codice e pensare: "Ehi, ho questo già in JavaScript!" Qui è lo stesso codice, questa volta in JavaScript: Container function () (this._managed = / * a prescindere dal tipo * /) Come illustrato nel Caso 2-8, si può avere riferimento a base di anatra o digitando il valore a base di anatra tipizzazione. Attuazione "generici" per JavaScript è come l'attuazione di un preprocessore, quindi vogliamo che il valore a base di anatra battitura. Nell'esempio di codice seguente mostra cosa può andare male quando il valore di mixare e anatra riferimento digitando insieme: proxy: funzione (ad esempio, funcIdentifier, newFunc) (if (! esempio [funcIdentifier]) (throw new Error ( "Impossibile proxy metodo inesistente (" + + funcIdentifier ")");) eval ( "var generatedOrigFunc =" + esempio [ funcIdentifier]. toString ()); eval ( "var generatedProxyFunc =" + newFunc.toString ());esempio [funcIdentifier] = function () ( origFunc var = generatedOrigFunc; proxyFunc var = generatedProxyFunc;var args = new Array (); for (var c1 = 0; c1 <arguments.length; c1 + +) (args.push (argomenti [c1]);) args.push (origFunc); args.push (argomenti) ; proxyFunc.apply (this, args);)) Questo codice sorgente ne è un esempio di come non dare una attuazione pattern Proxy. Il problema dell'attuazione è illustrato nel codice in grassetto. All'inizio del codice in grassetto sono due affermazioni eval, che stanno generando i valori delle variabili generatedOrigFunc e generatedProxyFunc. Nel caso embedded funzione [funcIdentifier], le variabili si fa riferimento, e grazie alla chiusura, le variabili origFunc e proxyFunc farà riferimento le variabili corrette. Il concetto di pattern Proxy è che quando un metodo si chiama, chiama prima la funzione di riferimento da proxyFunc, che poi chiama la funzione a cui fa riferimento origFunc. Perché proxyFunc è chiamato per primo, ha la capacità di preprocessare o post elaborazione dei dati.
Il codice scritto come si lavora con una singola applicazione del pattern Proxy, ma avrà esito negativo se il pattern proxy viene applicata più volte. Per esempio, immaginate l'inserimento del codice generato il pattern Proxy su un modello già applicato Proxy. Questo codice è un esempio di un dettaglio del tuo "pezzo di codice in JavaScript. Esaminare attentamente le dichiarazioni delle variabili generatedOrigFunc e generatedProxyFunc cosa sono riferimenti? Per capire l'errore, pensare al codice proxy a sinistra come attuazione proxy 1 (PI1) e il codice a destra come attuazione proxy 2 (PI2). Se un chiamante generiche sono state chiamate a PI1, la seguente sequenza di eventi si terranno: 1. PI1 è chiamato. 2. ProxyFunc PI1 si chiama. 3. OrigFunc PI1 si chiama, che è PI2. 4. ProxyFunc PI2 si chiama, che è proxyFunc PI1's. 5. ProxyFunc PI1 si chiama, chiama origFunc PI1's. Dopo questi passaggi, si noterà che la ricorsione è in corso. La ricorsione è a causa del modo in cui le variabili generatedOrigFunc e generatedProxyFunc sono dichiarate. Queste variabili sono dichiarate, senza alcuna possibilità e perché la funzione di proxy campo di applicazione è amix di utilizzare riferimenti con il codice eval. Questa è una cattiva pratica, in quanto alcune variabili verrà serializzato e altri no. Vogliamo una soluzione come il preprocessore C, dove "generici" rappresentano la sostituzione di un identificatore con un tipo fisico desiderato. Noi non vogliamo un sistema di templating. Un sistema di template sarebbe descritto come segue: <% for (var count = 0; count <10; count + +) (%> serie [<% = conta%>] = GenSeries (<% = conta%>) <%}%> Questo non è ciò che vogliamo, perché la leggibilità del codice diminuisce drasticamente, e in generale non è necessario. JavaScript è un linguaggio dinamico di codice, e molti dei template costrutti possono essere implementati nel codice. E 'più difficile da attuare il cambiamento di una variabile di usare riferimenti all'uso dei valori. Soluzione Per scopi illustrativi, let's semplificare il pattern Proxy a un metodo unico con una sola chiamata metodo, come segue. Fonte: / website / ROOT / ajaxrecipes / javascript / generics.html funzione EmbeddedReplace () (var func = __toCall; info ( "Replace", "ciao"); func ();) La funzione EmbeddedReplace ha dichiarato una variabile locale, func. La funzione di riferimenti locali variabile la variabile __toCall. Dopo la dichiarazione delle funzioni è una chiamata alla funzione info. La chiamata di funzione ultima è quella di funzione, che è una chiamata di funzione per __toCall. Guardando esclusivamente a uso di __toCall, non si sa molto circa la dichiarazione di diverso è una variabile globale. Per il campo di applicazione di questa ricetta, __toCall è un identificatore che verrà sostituito. Una chiamata di espansione JavaScript sarebbe la seguente (I'll discutere i dettagli a breve): EmbeddedReplace = Generics.expand (EmbeddedReplace, (__toCall: function () (info ( "sostituito in Replace", "ciao ");}}); EmbeddedReplace (); Il metodo Generics.expand ha due parametri. Il primo parametro è la funzione EmbeddedReplace, che avrà la sua attuazione modificati. Il secondo parametro è la struttura oggetto che rappresenta ciò che gli identificatori verrà sostituito. La struttura oggetto è definito in modo che l'identificatore di proprietà definisce l'identificatore di essere sostituito, e il valore associato con la proprietà è il valore sostituito.Nota Tutto il codice qui presentato utilizza le tecniche di programmazione JavaScript standard. Non hai bisogno di utilizzare buffer speciali o tag che confondono i redattori e rendere più complicato da costruire affidabili, codice mantenibile. Quando il metodo Generics.expand ha terminato l'esecuzione, la seguente dichiarazione di funzione viene generato: GeneratedEmbeddedReplace function () (var func = (function () (info ( "sostituito in Replace", "ciao");)); info ( "Replace", "ciao"); func ();) Il buffer generato è quello che ci aspettiamo, e il comportamento è quello che ci aspettiamo. GeneratedEmbeddedReplace quando viene chiamato, la funzione viene chiamata incorporati. Per l'amor di confronto, diamo un'occhiata a come questo avrebbe potuto funzionare senza l'espansione di una funzione incorporata: __toCall function () (info ( "sostituito in Replace", "ciao");); EmbeddedReplace function () (var func = __toCall; info ( "Replace", "ciao"); func ();) GeneratedEmbeddedReplace chiamante chiamare la funzione __toCall e si comportano, guardare, e si sentono come l'esempio espanso. Ma perché si deve fare questo? Il riferimento a base di espansione non è un espansione a tutti. Lei ha semplicemente creato una variabile globale (__toCall) che può essere assegnato in fase di runtime. Con un valore di espansione di tipo, si può avere molteplici funzioni con diverse funzionalità che non sono in conflitto con un altro. Perché il comportamento è determinato dalla cessione, è possibile eseguire nella situazione descritta nel Caso 2-14, dove le funzioni sono condivise. Se le funzioni sono condivise, quindi un conflitto può sorgere se la funzionalità della funzione è alterata. O si può avere una situazione in cui una funzione fa una cosa sola volta, e un'altra cosa un'altra volta. Per illustrare il "fare una cosa adesso, ma non più tardi" problema, si consideri lo scenario seguente codice: __toCall function () (info ( "sostituito in Replace", "ciao");); EmbeddedReplace function () (info ( "Replace", "ciao"); __toCall ();) function chiamante () (EmbeddedReplace (); __toCall = function () () EmbeddedReplace ();) Chiamante quando viene chiamato, la prima chiamata a EmbeddedReplace risultati in qualche uscita. Allora __toCall viene riassegnato, e chiamando i risultati nuovo EmbeddedReplace in uscita altri vengono generati. Forse questo è l'effetto desiderato, ma molto probabilmente non lo è. Un modo per risolvere il problema è quello di restringere la utilizzo delle capacità dinamica di JavaScript. Tuttavia, a questo punto mi chiedo, perché stai usando JavaScript e Ajax? La mia opinione è che JavaScript e Ajax rappresentano un'evoluzione nella programmazione e quindi si dovrebbero usare la loro capacità di comportamento dinamico. EmbeddedReplace era stato ampliato, quindi modificando __toCall non avrebbe avuto alcun effetto sulla EmbeddedReplace. E questo è ciò che è necessario prendere in considerazione durante la scrittura di codice JavaScript. A volte si usa riferimenti, e qualche volta si useranno le espansioni, come quelli proposti dalla generici JavaScript. Vorrei aggiungere un altro punto. Non è una regola empirica, ma una idea che in contesti specifici può avere il suo posto. Immaginate di scrivere una domanda in cui il pagamento è calcolato sulla base del tasso di interesse corrente al giorno. Nella maggior parte dei linguaggi di programmazione, il pagamento di interesse corrente sarebbe una variabile che potrebbe essere caricati e assegnato in fase di runtime. Utilizzando i generici JavaScript e il comportamento basato su codice, il codice seguente espanso può essere utilizzato: CalculateInterestPayment function (importo) (return amount * 0,04;) Questo codice è quello che altri linguaggi di programmazione di chiamata hard-coded. È considerato hardcoded perché il numero 0,04 è compilato nel ricorso e non può essere modificato. Ma ecco il vantaggio del comportamento basato su codice: using generics JavaScript, il valore hardcoded è banale al cambiamento, e mantenendo per un modello tradizionale di programmazione quando non c'è bisogno di non fare alcun senso. Si utilizza una strategia a livello di codice di programmazione, quando i dati sono read-mostly, O quando i dati vengono letti più spesso di quanto è scritto. Avendo i dati a livello di codice può essere un guadagno di prestazioni, offrono una maggiore flessibilità, e semplificare l'algoritmo. Utilizzando i generici JavaScript oggi, il calcolo degli interessi è un semplice numero, ma domani il calcolo potrebbe essere un calcolo complesso che utilizza una regola scorrevoli in base alla quantità che viene elaborato. Dopo aver spiegato il perché, come, e quando di JavaScript "generici", I'll coprono ora come JavaScript "generici" sono attuate. E 'un processo molto semplice, che implica la ricerca e la sostituzione di un identificatore in un buffer. La piena attuazione è il seguente. Fonte: / website / root / scripts / Common.js Generics var = (expand: function (toProcess, itemsToInject) (var bufferToProcess = ops.singleSerialize (toProcess), per (itemToReplace in itemsToInject) (var recurFind = function (startIndex) (var offset = bufferToProcess.indexOf (itemToReplace, startIndex); if (offset == -1) (return;) sinistra var = bufferToProcess.slice (0, offset); diritto var = bufferToProcess.slice (offset + itemToReplace.length); mezzo var = ops.simpleSerialize (itemsToInject [itemToReplace]); bufferToProcess = sinistra + destra + centro; offset + +; recurFind (offset);) recurFind (0);) genBuffer var = "cls var =" + bufferToProcess + ";" eval (genBuffer); cls return;)) Per espandere un oggetto JavaScript, l'oggetto deve prima essere convertito in un buffer. Il ops.singleSerialize metodo converte qualsiasi oggetto JavaScript in un buffer, e, nel caso dell'esempio precedente, si converte il parametro toProcess in un buffer. Dopo la conversione in un buffer, il processo di trovare un identificatore e la sua sostituzione con un ciclo viene avviato. Ad ogni iterazione del ciclo memorizzato nella variabile itemToReplace è l'identificatore di sostituire in bufferToProcess. Nel ciclo, i riferimenti recurToFind variabile di una funzione che è chiamato in modo ricorsivo. Lo scopo della ricorsione è incrementale ricerca del buffer per l'identificatore di sostituire. Nota Strategia di ricorsione è coperto nel Caso 2-6. Per trovare un identificatore all'interno bufferToProcess, il metodo indexOf viene utilizzato. indexOf ha due parametri: il primo parametro è l'identificatore di trovare, e il secondo parametro è l'indice per avviare la ricerca. IndexOf se non trova un identificativo, quindi un valore di -1 viene restituito e la ricorsione si ferma. Se indexOf trova l'identificativo, l'indice del luogo in cui inizia l'identificatore viene restituito. Dopo aver constatato l'identificatore, il buffer è suddiviso in una parte sinistra e una parte destra. La parte sinistra e destra sono combinati con il testo da sostituire, e una bufferToProcess viene creato un nuovo. Identificatori Quando non si trovano, il buffer è considerato ampliato. L'ultimo passo rimanente è di convertire il buffer in un'istanza di oggetto. Convertire il buffer è un problema, perché l'esecuzione del return eval (bufferToProcess) comando potrebbe portare a scenari imprevedibili. Per effettuare la conversione prevedibile, bufferToProcess è concatenato con un incarico alla variabile cls. Poi, quando il nuovo buffer viene eseguita mediante l'istruzione eval, la CLS variabile locale viene creata un'istanza, e può essere restituito al chiamante. Il codice per ampliare e implementare JavaScript "generici" è relativamente semplice, ma gli effetti sono profonde. Quando si utilizza JavaScript "generici", a mantenere i seguenti punti in mente: • riferimenti JavaScript sono più facili da attuare e, in molti casi, abbastanza bene. Tuttavia, come indicato nella ricetta 2-14, i riferimenti, non rappresentano un dettaglio del tuo "problema". • È necessario prendere una decisione tra l'utilizzo di riferimento per tutto e per tutto usando eval. "Tutto" qui non si riferisce ad una intera applicazione, ma tutto nell'ambito di un tipo o di una funzione. Non mescolare i riferimenti con l'istruzione eval; farlo causerà problemi. • Nel complesso, il codice è più robusto e stabile, se si utilizza eval-come le tecniche. I riferimenti di lavoro per casi semplici, ma se il vostro embeds codice codice più volte, poi gli errori potrebbero derivare. • Con riferimento, si corre il rischio che i pezzi più di codice di riferimento lo stesso codice e le stesse variabili. Questo può causare la corruzione e la non deve essere sottovalutata. • Utilizzo di eval-simili dichiarazioni rende più facile il debug, perché se si serializzare una funzione o un oggetto, si otterrà lo stato attuale del codice e quindi essere in grado di seguire ciò che è il problema. Se si utilizza riferimenti, avrete bisogno di un debugger, e con il codice che determina il suo comportamento in fase di runtime, il debugging può essere noioso. • Utilizzo di JavaScript "generici", come descritto in questa ricetta è IDE-friendly, perché non si sta creando buffer a mano, e non sono la scrittura di codice con i tag speciali che IDE, come X-sviluppo, Visual Studio, Komodo, e Visual SlickEdit fare non capire. un articolo presentato da Sonja Lande Disclaimer:Il nostro sito non è responsabile per il contenuto di questo articolo. Webarticles è una risorsa gratuita di informazioni. Importante: Questo articolo "esecuzione JavaScript Generics" è stato tradotto da un software automatico. Ci dispiace per eventuali errori di ortografia che possono essersi verificati. Grazie per la vostra comprensione.
|
|||||
| Online: 312 users browsing the articles directory |
|
|