Mise en œuvre JavaScript Generics

Problème Vous voulez mettre en œuvre les «génériques» en JavaScript. Langages de programmation Théorie tels que Java et C # ont un concept de programmation appelé génériques. L'idée derrière génériques est d'écrire du code de telle manière que l'identifiant est défini comme un type général qui est manipulé dans une classe ou une méthode. Par exemple, le code suivant serait une classe générique en Java ou C #:

classe Container  Tapez (_MANAGED;)

La classe Container a un paramètre générique, type, qui peut référencer n'importe quel autre type. Et dans la déclaration de conteneur, le membre _MANAGED données est du même type que le paramètre générique. Dans une notion abstraite, Container gère tout type. Un programmeur JavaScript pourrait regarder le code, vous pensez: "Hé, j'ai déjà présent dans JavaScript!" Voici le même code, cette fois en JavaScript:

fonction Container () (this._managed = / * quel que soit le type * /)

Comme illustré dans la recette 2-8, vous pouvez avoir de référence de typage fondée sur le canard ou la valeur de typage fondée sur le canard. Mettre en œuvre les «génériques» pour JavaScript, c'est comme mettre en œuvre un préprocesseur, donc nous voulons basée sur la valeur tapant canard. L'exemple de code suivant montre ce qui peut arriver quand on mélange la valeur et le canard de référence tapant ensemble:

proxy: fonction (par exemple, funcIdentifier, newfunc) (if (! exemple [funcIdentifier]) (throw new Error ( "Impossible de proxy méthode inexistante (" + + funcIdentifier ")");) eval ( "var generatedOrigFunc =" + instance [ funcIdentifier]. toString ()); eval ( "var generatedProxyFunc =" + newFunc.toString ());par exemple [funcIdentifier] = function () ( origFunc var = generatedOrigFunc; proxyFunc var = generatedProxyFunc;var args = new Array (); for (var c1 = 0; C1 <arguments.length; C1 + +) (args.push (arguments [c1]);) args.push (origFunc); args.push (arguments) ; proxyFunc.apply (this, args);)),

Ce code source est un exemple de comment ne pas écrire une implémentation du modèle de procuration. Le problème de la mise en œuvre est indiqué dans le code gras. Au début du code gras sont deux déclarations eval, qui génèrent les valeurs des variables generatedOrigFunc et generatedProxyFunc. En l'occurrence fonction intégrée [funcIdentifier], les variables sont référencées, et en raison de la fermeture, les variables origFunc et proxyFunc fera référence aux variables correctes. Le concept de la configuration du proxy est que lorsqu'une méthode est appelée, elle appelle d'abord la fonction référencée par proxyFunc, qui appelle ensuite la fonction référencée par origFunc. Parce proxyFunc est appelée en premier, il a la capacité de prétraitement ou de post-traitement des données.

  

Le code tel qu'il est écrit fonctionnera avec une seule application du modèle de procuration, mais elle ne réussira pas si le modèle de procuration est appliqué à plusieurs reprises. Par exemple, imaginez l'enrobage du code généré par le modèle de procuration sur un modèle de procuration déjà appliquées. Ce code est un exemple de Gotcha "" bout de code en JavaScript. Observez attentivement les déclarations des variables generatedOrigFunc et generatedProxyFunc Que font-ils le référencement? Pour comprendre l'erreur, pensez à le code proxy sur la gauche comme la mise en œuvre de procuration 1 (PI1) et le code sur le droit, comme la mise en oeuvre du proxy 2 (pi2). Si un appelant génériques ont été d'appeler PI1, la séquence suivante d'événements auront lieu:

1. PI1 est appelée.

2. ProxyFunc PI1 est appelée.

3. OrigFunc PI1 est appelée, ce qui est PI2.

4. ProxyFunc PI2 est appelée, ce qui est proxyFunc PI1's.

5. ProxyFunc PI1 est appelé, appelant origFunc PI1's. Après ces étapes, vous remarquerez que la récursivité est en cours. La récursivité est en raison de la façon dont les variables generatedOrigFunc et generatedProxyFunc sont déclarées. Ces variables sont déclarées sans aucune portée et parce que la fonction proxy est portée amix d'utiliser des références avec le code d'eval. Il s'agit d'une mauvaise pratique, car certaines variables seront sérialisés et d'autres pas. Nous voulons une solution comme le préprocesseur C, où les «génériques» représentent la physique en remplacement d'un identifiant avec un type désiré. Nous ne voulons pas d'un système de templates. Un système de templates serait décrite comme suit:

<% for (var count = 0; count <10; count + +) (%> Série [<% = count%>] = GenSeries (<% = count%>) <%}%>

Ce n'est pas ce que nous voulons parce que la lisibilité du code diminue considérablement, et en général, il n'est pas nécessaire. JavaScript est un langage dynamique de code, et la plupart des gabarits des constructions peuvent être mises en œuvre dans le code. Il est plus difficile à mettre en œuvre le changement d'une variable d'utiliser des références à l'utilisation de valeurs. Solution Pour fins d'illustration, nous allons simplifier la configuration du proxy à une méthode unique avec un seul appel de méthode, comme suit.

 Source: / website / ROOT / ajaxrecipes / javascript / fonction generics.html EmbeddedReplace () (var func = __toCall; info ( "Replace", "bonjour"); func ();)

La fonction EmbeddedReplace a déclaré une variable locale, Func. Les références à des variables locales fonction de la variable __toCall. Après la déclaration de fonction est un appel à la fonction info. Le dernier appel, la fonction est de fonction, qui est un appel de fonction pour __toCall. De s'intéresse uniquement à l'usage des __toCall, tu ne sais pas beaucoup sur la déclaration autres que c'est une variable globale. Pour le champ d'application de cette recette, __toCall est un identifiant qui sera remplacé. Un appel d'extension JavaScript serait comme suit (je vais en discuter les détails prochainement):

EmbeddedReplace = Generics.expand (EmbeddedReplace, (__toCall: function () (info ( "remplacé en Remplacer", "bonjour ");}}); EmbeddedReplace ();

La méthode Generics.expand a deux paramètres. Le premier paramètre est la fonction EmbeddedReplace, ce qui aura sa mise en oeuvre modifiée. Le deuxième paramètre est la structure d'objet qui représente ce que les identifiants seront remplacés. La structure de l'objet est défini tel que l'identificateur de propriété définit l'identifiant soit remplacé, et la valeur associée à la propriété est la valeur de remplacement.Note Tout le code présenté ici utilise des techniques de programmation standard JavaScript. Vous n'avez pas besoin d'utiliser des tampons ou des balises spéciales qui confondent les éditeurs et les rendre plus compliqué à construire du code fiable et maintenable. Lorsque la méthode Generics.expand a terminé son exécution, la déclaration de fonction suivant est généré:

GeneratedEmbeddedReplace fonction () (func var = (function () (info ( "remplacé en Remplacer", "bonjour");)); info ( "Replace", "bonjour"); func ();)

Le tampon générée est ce que nous attendons, et le comportement est ce que nous attendons. Lorsque GeneratedEmbeddedReplace est appelée, la fonction intégrée est appelé. À titre de comparaison, nous allons regarder comment cela pourrait avoir travaillé sans étendre une fonction embarquée:

__toCall function () (info ( "remplacé en Remplacer", "bonjour");) function EmbeddedReplace () (var func = __toCall; info ( "Replace", "bonjour"); func ();)

GeneratedEmbeddedReplace Calling appelle la fonction __toCall et de se comporter, regarder, et se sentir comme dans l'exemple développé. Mais pourquoi devriez-vous faire cela? La référence à base d'expansion n'est pas une extension à tous. Vous avez simplement créé une variable globale (__toCall) qui peut être attribué à l'exécution. Avec une valeur d'expansion type, vous pouvez avoir des fonctions multiples avec différentes fonctionnalités qui ne sont pas en contradiction avec l'un l'autre. Comme le comportement est déterminé par voie de cession, vous pouvez exécuter dans la situation décrite dans 2-14 Recette, où les fonctions sont partagées. Si les fonctions sont partagées, puis un conflit peut survenir si la fonctionnalité de la fonction est altérée. Ou vous pouvez avoir une situation où une fonction fait une chose une fois, et une autre chose d'un autre temps. Pour illustrer le «faire quelque chose aujourd'hui, mais pas au" problème, envisager le scénario de code suivant:

__toCall function () (info ( "remplacé en Remplacer", "bonjour");) function EmbeddedReplace () (info ( "Replace", "bonjour"); __toCall ();) function Caller () (EmbeddedReplace (); __toCall = function () () EmbeddedReplace ();)

Lorsque l'appelant est appelée, le premier appel à EmbeddedReplace résultats dans certains de sortie. Puis __toCall est réaffecté, et appelant les résultats EmbeddedReplace nouveau dans la production d'autres cours de création. C'est peut-être l'effet désiré, mais plus probablement qu'elle n'est pas. Une manière de résoudre le problème est de limiter votre utilisation de la capacité dynamique du langage JavaScript. Toutefois, à ce stade, je voudrais demander, pourquoi êtes-vous en utilisant JavaScript et Ajax? Mon avis est que JavaScript et Ajax représentent une évolution dans la programmation, et donc vous devriez utiliser leurs capacités de comportement dynamique.

EmbeddedReplace avait été élargi, modifiant, puis __toCall n'aurait eu aucun effet sur EmbeddedReplace. Et c'est ce que vous devez tenir compte lorsque vous écrivez du code JavaScript. Parfois, vous utilisez des références et, parfois, vous utiliserez l'expansion tels que ceux proposés par les génériques JavaScript. Je voudrais ajouter un dernier point. Ce n'est pas une règle du pouce, mais une idée qui, dans certains contextes peut avoir sa place. Imaginez écrit une application où un paiement est calculé sur la base du rythme actuel d'intérêt quotidien. Dans la plupart des langages de programmation, le paiement d'intérêts en cours serait une variable qui serait chargé et affecté à l'exécution. En utilisant les génériques JavaScript et code axé sur le comportement, le code développé ci-après pourraient être utilisés:

fonction CalculateInterestPayment (montant) (montant return * 0.04;)

Ce code est ce autres langages de programmation appel codées en dur. On pense qu'il est codé en dur parce que le numéro 0.04 est compilé dans l'application et ne peut pas être changé. Mais voici l'avantage du comportement code axé sur: l'utilisation des médicaments génériques JavaScript, la valeur codée en dur est futile de changer, et en maintenant à un modèle de programmation traditionnel alors que vous n'avez pas besoin ne fait aucun sens que ce soit.

Vous utilisez une stratégie codée en dur de programmation lorsque les données sont principalement en lecture, Ou lorsque les données sont lues plus souvent que cela est écrit. Ayant les données codées en dur peut être un gain de performance, offrent plus de flexibilité, et de simplifier l'algorithme. JavaScript en utilisant les génériques d'aujourd'hui, le calcul des intérêts est un simple numéro, mais demain, le calcul peut être un calcul complexe qui utilise une règle à calcul basé sur le montant qui est traitée. Après avoir expliqué le pourquoi, comment et quand de JavaScript «génériques», Je vais maintenant couvrir comment JavaScript «génériques» sont mises en œuvre. C'est un processus très simple qui consiste à rechercher et remplacer un identifiant dans une mémoire tampon. La mise en œuvre complète est la suivante.

 Source: / website / root / scripts / Common.js Generics var = (expand: function (toProcess, itemsToInject) (var bufferToProcess = ops.singleSerialize (toProcess) for (itemToReplace dans itemsToInject) (var recurFind = function (startIndex) (var offset = bufferToProcess.indexOf (itemToReplace, startIndex); if (offset == -1) (return;) var gauche = bufferToProcess.slice (0, offset); droit var = bufferToProcess.slice (offset + itemToReplace.length); Moyen-var = ops.simpleSerialize (itemsToInject [itemToReplace]); bufferToProcess = + gauche + droite, au milieu; offset + +; recurFind (offset);) recurFind (0);) genBuffer var = "cls var =" + bufferToProcess + ";" eval (genBuffer); CLS return;))

Pour développer un objet JavaScript, l'objet doit d'abord être convertis à un tampon. Le ops.singleSerialize méthode convertit n'importe quel objet JavaScript dans le buffer, et dans le cas de l'exemple précédent, il convertit le paramètre toProcess dans un buffer. Après la conversion à une mémoire tampon, le processus de recherche d'un identifiant et son remplacement en utilisant une boucle est démarré. À chaque itération de la boucle stockée dans la variable itemToReplace est l'identifiant de remplacer dans bufferToProcess. Dans la boucle, les références recurToFind variable d'une fonction qui est appelée récursivement. Le but de la récurrence est de rechercher progressivement la mémoire tampon pour l'identificateur à remplacer.

Note Recursion stratégie est couverte dans la recette 2-6. Pour trouver un identifiant dans bufferToProcess, la méthode indexOf est utilisée. indexOf a deux paramètres: le premier paramètre est l'identifiant de trouver, et le second paramètre est l'indice de début de recherche. Si indexOf ne trouve pas un identificateur, alors une valeur de -1 est retourné et les arrêts récursivité. Si indexOf trouve l'identifiant, puis l'indice de l'endroit où commence l'identifiant est retourné. Ayant trouvé l'identifiant, le tampon est divisé en une partie gauche et une partie droite. Les parties gauche et droite sont combinées avec le texte qui sera remplacé, et un bufferToProcess nouvelles sont créées. Lorsque aucun identificateur n'est trouvé, le tampon est considéré comme élargi. La dernière étape restante est de convertir la mémoire tampon dans une instance d'objet. Conversion de la mémoire tampon est un problème parce que l'exécution de la eval retour (bufferToProcess) commande pourrait conduire à des scénarios imprévisibles. Pour effectuer la conversion prévisible, bufferToProcess est concaténé avec une affectation à la variable CLS. Puis, quand la nouvelle mémoire tampon est exécutée en utilisant l'instruction eval, le CEMAT variable locale est instancié, et il peut être renvoyé à l'appelant. Le code pour développer et mettre en œuvre JavaScript "génériques" est relativement simple, mais les effets sont profonds. Lors de l'utilisation des génériques "JavaScript", gardez les points suivants à l'esprit:

• références JavaScript sont plus faciles à mettre en œuvre et, dans de nombreux cas, assez bon. Toutefois, comme l'a souligné dans la recette 2-14, références ne posent un Gotcha "" problème.

• Vous devez faire un choix entre l'utilisation des références pour tout et utiliser eval pour tout. "Everything" ici ne se réfère pas à une application entière, mais tout dans le champ d'un type ou une fonction. Ne mélangez pas les références à la déclaration eval; cela lui permet de poser des problèmes.

• Dans l'ensemble, votre code est plus robuste et plus stable si vous utilisez eval techniques semblables. Références de travail pour les cas simples, mais si votre temps de widgets code code multiple, des erreurs pourraient en résulter.

• Avec des références, vous courez le risque que de multiples fragments de code est la référence du même code et les mêmes variables. Cela peut entraîner la corruption et ne doit pas être sous-estimée.

Eval • utilisant comme les états rend le débogage plus facile, parce que si vous sérialiser une fonction ou un objet, vous obtiendrez l'état actuel du code et ainsi être en mesure de suivre ce qui est le problème. Si vous utilisez des références, vous aurez besoin d'un débogueur, et avec le code qui détermine son comportement lors de l'exécution, le débogage peut être fastidieux.

• Utilisation de JavaScript "génériques" comme décrit dans cette recette est IDE-friendly parce que vous ne créez pas de tampons à la main, et vous n'écrivez pas de code avec des balises spéciales qui environnements de développement tels que X-développement, Visual Studio, Komodo, et Visual SlickEdit ne pas comprendre.

un article présenté par Sonja Lande


Disclaimer:Notre site n'est pas responsable du contenu de cet article. Webarticles est une ressource d'information gratuite.
Important: Cet article «Mise en œuvre JavaScript génériques» a été traduit par un logiciel automatique. Nous nous sentons désolés pour les fautes d'orthographe que mai ont eu lieu. Nous vous remercions de votre compréhension.


Online: 240 users browsing the articles directory   


  

|