Umsetzung JavaScript GenericsProblem Sie implementieren möchten "Generika" in JavaScript. Theory Programmiersprachen wie Java und C #-Programmierung ein Konzept genannt haben Generika. Die Idee dahinter ist es, Generika-Code in einer Weise, dass ein Bezeichner als allgemeiner Art, die in einer Klasse oder Methode definiert ist, manipuliert zu schreiben. Zum Beispiel würde der folgende Code eine generische Klasse in Java oder C #: Container-Klasse Die Container-Klasse verfügt über eine generische Parameter, Typ, die jede andere Art verweisen können. Und in der Erklärung der Container, die _MANAGED Daten Mitglied ist vom gleichen Typ wie die generische Parameter. In einem abstrakten Begriff, verwaltet jede Art Container. Ein JavaScript-Programmierer können sich den Code anschauen und denken: "Hey, ich habe dies bereits in der JavaScript!" Hier ist der gleiche Code, diesmal in JavaScript: Container-Funktion () (this._managed = / * gleich welcher Art * /) Wie im Rezept 2-8 dargestellt, können Sie sich auf-basierte Duck Typing oder wertorientierte Duck Typing. Implementierung "Generika" für JavaScript ist wie die Durchführung einer Präprozessor, so wollen wir wertorientierte Duck Typing. Das folgende Codebeispiel zeigt, was schief gehen kann, wenn Sie mischen Wert und Fundstelle Duck Typing zusammen: Proxy: function (instance, funcIdentifier, newfunc) (if (! Instanz [funcIdentifier]) (throw new Error ( "Can't nicht vorhandenen Proxy-Methode (" + + funcIdentifier ")");) eval ( "var generatedOrigFunc =" + Instanz [ funcIdentifier]. toString ()); eval ( "var generatedProxyFunc =" + newFunc.toString ());Instanz [funcIdentifier] = function () ( var origFunc = generatedOrigFunc; var proxyFunc = generatedProxyFunc;var args = new Array (); for (var c1 = 0; c1 <arguments.length; c1 + +) (args.push (arguments [c1]);) args.push (origFunc); args.push (Argumente) ; proxyFunc.apply (this, args);)) Dieser Quellcode ist ein Beispiel dafür, wie man ein Proxy-Muster Umsetzung zu schreiben. Das Problem der Umsetzung wird in den kühnen Code dargestellt. Zu Beginn des kühnen Code eval gibt zwei Aussagen, welche die Werte der generatedOrigFunc und generatedProxyFunc Variablen generieren. Im Embedded-Funktion Instanz [funcIdentifier], die Variablen verwiesen wird, und aufgrund der Schließung, die origFunc und proxyFunc Variablen bezeichnet die korrekten Variablen. Das Konzept des Proxy-Muster ist, dass, wenn eine Methode aufgerufen wird, ruft es erste der Funktion verwiesen von proxyFunc, die ruft dann die Funktion durch origFunc verwiesen wird. Da proxyFunc heißt erstens, hat es die Fähigkeit zur Vorverarbeitung oder Nachbearbeitung der Daten.
Der Code geschrieben wird mit einer einzelnen Anwendung der Proxy-Muster arbeiten, aber es wird fehlschlagen, wenn der Proxy-Muster mehrfach angewendet wird. Zum Beispiel, stellen Sie den generierten Code Einbettung des Proxy-Muster auf einem bereits angewandten Proxy-Muster. Dieser Code ist ein Beispiel für eine "Gotcha" Stück Code in JavaScript. Genau auf die Erklärungen der generatedOrigFunc und generatedProxyFunc Variablen Sehen Sie, was sind sie verweisen? Um den Fehler zu verstehen, denken Sie an den Proxy-Code auf der linken als Proxy Umsetzung 1 (PI1) und den Code auf der rechten Seite als Proxy Umsetzung 2 (PI2). Wenn eine generische Anrufer waren zu nennen PI1, die folgende Sequenz von Ereignissen stattfinden würde: 1. PI1 aufgerufen wird. 2. PI1's proxyFunc aufgerufen wird. 3. PI1's origFunc aufgerufen, die PI2 ist. 4. PI2's proxyFunc aufgerufen, die PI1's proxyFunc ist. 5. PI1's proxyFunc aufgerufen wird, ruft origFunc PI1's. Nach diesen Schritten werden Sie feststellen, dass eine Rekursion stattfindet. Die Rekursion ist wegen der Art der generatedOrigFunc und generatedProxyFunc Variablen deklariert werden. Diese Variablen werden ohne Rahmen und erklärt, da die Proxy-Scope-Funktion ist amix der Verwendung von Referenzen mit eval-Code. Dies ist eine schlechte Praxis, da einige Variablen werden serialisiert und andere nicht. Wir wollen eine Lösung wie bei der C-Präprozessor, wo "Generika" stellen die physische anstelle eines Bezeichners mit einer gewünschten Typ. Wir wollen nicht, ein Template-System. Ein Template-System würde wie folgt beschrieben werden: <% for (var count = 0; count <10; count + +) (%> Serie [<% = count%>] = GenSeries (<% = count%>) <%}%> Dies ist nicht das, was wir wollen, weil die Lesbarkeit des Codes sinkt drastisch, und im Allgemeinen ist es nicht notwendig ist. JavaScript ist eine dynamische Code-Sprache, und viele der Template-Konstrukte können in Code umgesetzt werden. Es ist schwieriger, an die sich verändernden einer Variablen verwenden, Hinweise auf die Verwendung von Werten. Solution Zur Veranschaulichung umzusetzen, wollen wir vereinfachen die Proxy-Muster auf eine einzige Methode mit einem einzigen Methodenaufruf, wie folgt. Quelle: / website / ROOT / ajaxrecipes / javascript / generics.html Funktion EmbeddedReplace () (var func = __toCall info; ( "Ersetzen", "Hallo"); func ();) Die EmbeddedReplace Funktion hat eine lokale Variable, Funktion erklärt. Die Funktion bezieht sich auf die lokale Variable __toCall variabel. Nach der Erklärung der Funktion ist ein Aufruf an die Info-Funktion. Die letzte Funktion Aufforderung ist die Funktion, die eine Funktion aufrufen, __toCall ist. Von der ausschließliche Blick auf die Nutzung von __toCall, weiß man nicht viel über die Erklärung, außer es ist eine globale Variable. Für den Umfang dieses Rezepts ist __toCall eine Kennung, die ersetzt werden soll. Ein JavaScript-Erweiterung bezeichnen würde sich wie folgt (Ich werde die Einzelheiten zu besprechen kurz): EmbeddedReplace = Generics.expand (EmbeddedReplace, (__toCall: function () (info ( "in Ersetzen" ersetzt, "Hallo ");}}); EmbeddedReplace (); Die Generics.expand Methode hat zwei Parameter. Der erste Parameter ist die EmbeddedReplace Funktion, die seine Umsetzung verändert haben wird. Der zweite Parameter ist die Objekt-Struktur, die dem entspricht, was Kennzeichen ersetzt werden. Die Objekt-Struktur ist so definiert, dass die Eigenschaft Kennung die Kennung ersetzt werden definiert, und der Wert mit dem Eigentum verbunden ist, der Wert ersetzt.Hinweis Alle der hier vorgestellte Code verwendet Standard-JavaScript programmatischen Techniken. Sie brauchen keine speziellen Puffer oder Tags verwenden, dass die Redakteure verwirren und machen es kompliziert, zuverlässig, wartbar Code zu erstellen. Wenn das fertig ist Generics.expand Methode ausgeführt wird, wird die folgende Funktion generiert Erklärung: Funktion GeneratedEmbeddedReplace () (var func = (function () (info ( "in Ersetzen" ersetzt, "Hallo");)); info ( "Ersetzen", "Hallo"); func ();) Die erzeugten Puffer ist, was wir erwarten, und das Verhalten ist, was wir erwarten. Wenn GeneratedEmbeddedReplace aufgerufen wird, wird die eingebettete Funktion aufgerufen. Zum Vergleich, lassen Sie uns anschauen, wie sich dies ohne Erweiterung eine eingebettete Funktion gearbeitet haben: Funktion __toCall () (info ( "in Ersetzen" ersetzt, "Hallo");) function EmbeddedReplace () (var func = __toCall; info ( "Ersetzen", "Hallo"); func ();) Calling GeneratedEmbeddedReplace die __toCall Funktion aufrufen und sich verhalten, das Aussehen und das Gefühl, das erweiterte Beispiel. Aber warum sollte man das tun? Die Referenz-basierte Erweiterung ist nicht eine Erweiterung überhaupt. Sie haben einfach erstellt eine globale Variable (__toCall), die zur Laufzeit zugewiesen werden kann. Mit einem Wert vom Typ Erweiterung können Sie mehrere Funktionen mit unterschiedlichen Funktionen, die keinen Konflikt mit einander haben. Da Verhalten durch Zuweisung bestimmt wird, können Sie in die Situation in Rezept 2-14, beschrieben, wo Funktionen geteilt werden. Wenn Funktionen geteilt werden, dann ein Konflikt kann auftreten, wenn die Funktionalität der Funktion verändert wird. Oder Sie können eine Situation, wo eine Funktion ein, was man Zeit haben und eine andere Sache eine andere Zeit. Um zu veranschaulichen, die "noch etwas tun jetzt, aber nicht später" Problem, betrachten Sie den folgenden Code Szenario: Funktion __toCall () (info ( "in Ersetzen" ersetzt, "Hallo");) function EmbeddedReplace () (info ( "Ersetzen", "Hallo"); __toCall ();) Caller function () (EmbeddedReplace (); __toCall = function () () EmbeddedReplace ();) Wenn die Anrufer genannt wird, um den ersten Anruf Ergebnisse EmbeddedReplace in einigen Ausgang. Dann __toCall zugewiesen ist, und ruft erneut EmbeddedReplace Ergebnisse in anderen Ausgang wird erstellt. Vielleicht ist das die gewünschte Wirkung, aber wahrscheinlich ist es nicht. Ein Weg, um das Problem zu lösen ist, um Ihre Nutzung der dynamischen Fähigkeiten von JavaScript zu beschränken. Doch an diesem Punkt möchte ich fragen, warum sind Sie mit JavaScript und Ajax? Meine Meinung ist, dass JavaScript und Ajax eine Entwicklung in der Programmierung darstellen und somit sollten Sie ihre dynamische Verhalten Fähigkeiten nutzen. Hätte EmbeddedReplace erweitert wurde, bleibt eine Änderung __toCall hätte keine Auswirkungen auf EmbeddedReplace hatte. Und das ist das, was Sie beachten müssen, beim Schreiben von JavaScript-Code. Manchmal finden Sie Hinweise zu verwenden, und manchmal werden Sie Erweiterungen wie die von JavaScript Generika vorgeschlagene Verwendung. Ich möchte einen weiteren Punkt hinzufügen. Es ist nicht eine Faustregel, sondern eine Idee, die in bestimmten Zusammenhängen ihren Platz haben kann. Stellen Sie einen Antrag ausfüllen, wenn eine Zahlung auf der Grundlage der aktuellen Tageskurs Zinssatz zu berechnen. In den meisten Programmiersprachen dürfte die derzeitige Zinszahlung eine Variable, die geladen werden und würde zur Laufzeit zugeordnet. Mit Hilfe von JavaScript Generika und Verhaltensanalyse-Code, könnten die folgenden erweiterten Code verwendet werden: Funktion CalculateInterestPayment (Anzahl) (return betrag * 0.04;) Dieser Code ist das, was andere Programmiersprachen nennen hartcodierte. Sie gilt als codiert, weil die Zahl 0,04 in die Anwendung kompiliert wird und kann nicht geändert werden. Aber hier ist der Vorteil der Verhaltensanalyse-Code: using JavaScript Generika, die hartcodierte Wert ist trivial zu verändern, zu halten und zu einem traditionellen Programmiermodell, wenn Sie brauchen nicht macht keinen Sinn, immer. Sie verwenden eine hartcodierte Programmstrategie, wenn die Daten lesen, meistOder wenn die Daten häufiger zu lesen, als es geschrieben wird. Nachdem die Daten hartcodierte kann eine Leistung zu gewinnen, bieten mehr Flexibilität und Vereinfachung des Algorithmus. Mit Hilfe von JavaScript Generika heute ist die Verzinsung eine einfache Zahl, aber morgen bei der Berechnung könnte eine Verbindung Berechnung, dass ein Rechenschieber auf den Betrag, der verarbeitet wird, abhängig macht. Gestützt erklärt, warum, wie und wann der JavaScript "Generika" Ich werde jetzt behandeln, wie JavaScript "Generika" umgesetzt werden. Es ist ein sehr einfacher Prozess, der Benutzer und beinhaltet anstelle eines Bezeichners in einem Puffer. Die vollständige Umsetzung ist wie folgt. Quelle: / website / root / scripts / var Common.js Generics = (erweitern: function (toProcess, itemsToInject) (var bufferToProcess = (toProcess) ops.singleSerialize for (itemToReplace in itemsToInject) (var recurFind = function (startIndex) (var offset = bufferToProcess.indexOf (itemToReplace, startIndex); if (offset == -1) (return;) var left = bufferToProcess.slice (0, offset); var rechts = bufferToProcess.slice (Offset + itemToReplace.length); var Mitte = ops.simpleSerialize (itemsToInject [itemToReplace]); bufferToProcess = left + middle + right; offset + +; recurFind (offset);) recurFind (0);) var genBuffer = "var cls =" + bufferToProcess + ""; eval (genBuffer); return cls;)) Um ein JavaScript-Objekt zu erweitern, hat das Objekt zunächst in einen Puffer umgewandelt werden. Die ops.singleSerialize Methode konvertiert alle JavaScript-Objekt in einen Puffer, und im Falle der vorherigen Beispiel, wandelt es die toProcess Parameter in einen Puffer. Nach der Umstellung auf einen Puffer, ist der Prozess der Suche nach einer Kennung und ersetzt sie mit einer Schleife gestartet. Mit jedem Durchlauf der Schleife gespeichert in der itemToReplace Variable ist die Kennung, die in bufferToProcess ersetzen. In der Schleife, die recurToFind Variable verweist auf eine Funktion, die rekursiv aufgerufen wird. Der Zweck der Rekursion wird schrittweise der Puffer für die Kennung zu ersetzen suchen. Hinweis Rekursion Strategie wird in Rezept 2-6 abgedeckt. Um eine Kennung innerhalb bufferToProcess zu finden, ist die indexOf Methode verwendet. indexOf hat zwei Parameter: die erste Parameter ist die Kennung zu finden, und der zweite Parameter ist die Index-Suche zu starten. Wenn indexOf nicht findet eine Kennung, dann ein Wert von -1 zurückgegeben und die Rekursion stoppt. Wenn indexOf findet das Kennzeichen, wird der Index, wo beginnt die Kennung zurückgegeben wird. Nachdem sie festgestellt hatten das Kennzeichen, wird der Puffer in einen linken Teil aufzuspalten und einen rechten Teil. Die linke und rechte Teile sind mit dem Text kombiniert, um ersetzt werden, und eine neue bufferToProcess erstellt wird. Wenn keine Identifikatoren gefunden werden, die als Puffer wird erweitert. Die letzte Schritt besteht darin, den Puffer in eine Objekt-Instanz zu konvertieren. Die Umwandlung der Puffer ist ein Problem, weil die Ausführung der Rückkehr eval (bufferToProcess) Befehl aus, um unvorhersehbare Szenarien führen könnte. Um die Umwandlung vorhersehbar, bufferToProcess ist mit einer Zuordnung zu den cls Variable hinzugefügt. Dann, wenn die neuen Puffer ausgeführt wird, mit der eval-Anweisung wird die lokale Variable cls instanziiert, und es kann an den Aufrufer zurückgegeben werden. Der Code zur Expansion und zur Umsetzung JavaScript "Generika" ist relativ einfach, aber die Auswirkungen sind tiefgreifend. Bei der Verwendung von JavaScript "Generika", sollten Sie die folgenden Punkte beachten: • JavaScript Referenzen sind einfacher zu implementieren und in vielen Fällen gut genug. Jedoch, wie bereits in Rezept 2-14, do verweist auf eine "gotcha darstellen" Problem. • Sie haben eine Entscheidung zwischen der Verwendung von Referenzen für alles und für alles, was mit eval zu machen. "Alles" hier bezieht sich nicht auf die gesamte Anwendung, aber alles im Rahmen einer Art oder Funktion. Mischen Sie keine Hinweise mit der eval-Anweisung, da dies zu Problemen führen wird. • Insgesamt ist Ihr Code robust und stabil, wenn Sie eval-ähnliche Techniken zu verwenden. Referenzen Arbeit für einfache Fälle, aber wenn Sie Ihren Code einschließen Code mehrfach, dann Fehler führen könnte. • Mit Referenzen, laufen Sie Gefahr, dass mehrere Teile des Codes wird auf den gleichen Code und die gleichen Variablen. Dies kann Ursache und Korruption sollte nicht unterschätzt werden. • Mit eval-Anweisungen wie das Debugging erleichtert, denn wenn man eine Funktion oder ein Objekt zu serialisieren, erhalten Sie den aktuellen Zustand des Codes zu erhalten und somit in der Lage zu folgen, was das Problem ist. Wenn Sie Verweise verwenden, werden Sie ein Debugger benötigen, und mit dem Code, dass ihr Verhalten zur Laufzeit bestimmt, kann das Debuggen langweilig sein. • Mit Hilfe von JavaScript "Generika", wie in diesem Rezept ist IDE-freundlich, weil Sie nicht schaffen Puffer von Hand, und du bist nicht das Schreiben von Code mit speziellen Tags, die IDEs wie X-Entwicklung, Visual Studio, Komodo und Visual SlickEdit tun nicht verstehen. Ein Artikel eingereicht von Sonja Lande Disclaimer:Unsere Website ist nicht verantwortlich für den Inhalt dieses Artikels. Webarticles ist eine kostenlose Informationsquelle. Wichtig: Dieser Artikel "Implementing JavaScript Generics" wurde durch ein automatisches Software übersetzt. Wir fühlen uns leid für alle Rechtschreibfehler, die möglicherweise aufgetreten sind. Vielen Dank für Ihr Verständnis.
|
|||||
| Online: 455 users browsing the articles directory |
|
|