Heim > Artikel > Web-Frontend > Einführung in ein einfaches JavaScript-Klassen-Framework_Grundkenntnisse
Als ich das in Arbeit befindliche JavaScript-Buch schrieb, habe ich viel Zeit mit dem JavaScript-Vererbungssystem verbracht und dabei verschiedene Lösungen zur Simulation der klassischen Klassenvererbung studiert. Unter diesen technischen Lösungen bewundere ich die Implementierung von base2 und Prototype am meisten.
Aus diesen Lösungen sollte ein Framework mit seiner ideologischen Konnotation abgeleitet werden. Das Framework muss einfach, wiederverwendbar, leicht verständlich und unabhängig von Abhängigkeiten sein. Hier sind Anwendungsbeispiele:
var Person = Class. extend ( { init: function (isDancing ) { this. dancing = isDancing; }, dance: function ( ) { return this. dancing; } } ); var Ninja = Person.extend({ init: function(){ this._super( false ); }, dance: function(){ // Call the inherited version of dance() return this._super(); }, swingSword: function(){ return true; } }); var p = new Person(true); p.dance(); // => true var n = new Ninja(); n.dance(); // => false n.swingSword(); // => true // Should all be true p instanceof Person && p instanceof Class && n instanceof Ninja && n instanceof Person && n instanceof Class
Es gibt ein paar Dinge zu beachten:
Sehr zufrieden mit den Ergebnissen: Die Klassendefinition wurde strukturiert, die Einzelvererbung wurde beibehalten und es konnten Superklassenmethoden aufgerufen werden.
Einfache Klassenerstellung und -vererbung
Das Folgende ist die Implementierung (leicht zu lesen und mit Kommentaren), etwa 25 Zeilen. Vorschläge sind willkommen und willkommen.
/* Simple JavaScript Inheritance * By John Resig http://ejohn.org/ * MIT Licensed. */ // Inspired by base2 and Prototype ( function ( ) { var initializing = false, fnTest = /xyz/.test(function(){xyz;}) ? /\b_super\b/ : /.*/; // The base Class implementation (does nothing) this.Class = function(){}; // Create a new Class that inherits from this class Class.extend = function(prop) { var _super = this.prototype; // Instantiate a base class (but only create the instance, // don't run the init constructor) initializing = true; var prototype = new this(); initializing = false; // Copy the properties over onto the new prototype for (var name in prop) { // Check if we're overwriting an existing function prototype[name] = typeof prop[name] == "function" && typeof _super[name] == "function" && fnTest.test(prop[name]) ? (function(name, fn){ return function() { var tmp = this._super; // Add a new ._super() method that is the same method // but on the super-class this._super = _super[name]; // The method only need to be bound temporarily, so we // remove it when we're done executing var ret = fn.apply(this, arguments); this._super = tmp; return ret; }; })(name, prop[name]) : prop[name]; } // The dummy class constructor function Class() { // All construction is actually done in the init method if ( !initializing && this.init ) this.init.apply(this, arguments); } // Populate our constructed prototype object Class.prototype = prototype; // Enforce the constructor to be what we expect Class.prototype.constructor = Class; // And make this class extendable Class.extend = arguments.callee; return Class; }; })();
Davon sind „Initialisieren/Init nicht aufrufen“ und „_super-Methode erstellen“ die schwierigsten. Als nächstes werde ich eine kurze Einführung dazu geben, damit jeder den Implementierungsmechanismus besser verstehen kann.
Initialisierung
Um die Vererbungsmethode des Funktionsprototyps zu veranschaulichen, schauen wir uns zunächst den traditionellen Implementierungsprozess an, der darin besteht, das Prototypattribut der Unterklasse auf eine Instanz der übergeordneten Klasse zu verweisen. Wie unten gezeigt:
function Person ( ) { } function Ninja ( ) { } Ninja. prototype = new Person ( ); // Allows for instanceof to work: (new Ninja()) instanceof Person
Die Herausforderung hierbei besteht jedoch darin, dass wir nur die Wirkung von „instatnceOf“ erzielen möchten, ohne die Konsequenzen der Instanziierung einer Person und des Aufrufs ihres Konstruktors. Um dies zu verhindern, legen Sie im Code einen bool-Parameter fest, dessen Wert nur dann wahr ist, wenn die übergeordnete Klasse instanziiert und für die Prototypeigenschaft der untergeordneten Klasse konfiguriert wird. Der Zweck dieser Verarbeitung besteht darin, den Unterschied zwischen dem Aufruf des Konstruktors während der echten Instanziierung und der Designvererbung und dem anschließenden Aufruf der Init-Methode während der echten Instanziierung zu unterscheiden:
if ( !initializing ) this.init.apply(this, arguments);
Es lohnt sich, besondere Aufmerksamkeit darauf zu richten, da in der Init-Funktion möglicherweise recht ressourcenintensiver Code ausgeführt wird (z. B. das Herstellen einer Verbindung zum Server, das Erstellen von DOM-Elementen usw. kann niemand vorhersagen), sodass dies unbedingt erforderlich ist einen Unterschied machen.
Super Methode
Bei der Verwendung der Vererbung besteht die häufigste Anforderung darin, dass die Unterklasse auf die überschriebenen Methoden der Oberklasse zugreifen kann. In dieser Implementierung besteht die endgültige Lösung darin, eine temporäre Methode (._super) bereitzustellen, die auf die Methode der Oberklasse verweist und auf die nur in der Methode der Unterklasse zugegriffen werden kann.
var Person = Class. extend ( { init: function (isDancing ) { this. dancing = isDancing; } } ); var Ninja = Person.extend({ init: function(){ this._super( false ); } }); var p = new Person(true); p.dancing; // => true var n = new Ninja(); n.dancing; // => false
Die Implementierung dieser Funktionalität erfordert mehrere Schritte. Zuerst verwenden wir „extend“, um die grundlegende Person-Instanz (Klasseninstanz, deren Konstruktionsprozess wir oben erwähnt haben) mit dem Literalobjekt (Funktionsparameter von Person.extend()) zusammenzuführen. Während des Zusammenführungsprozesses wurde eine einfache Prüfung durchgeführt: Überprüfen Sie zunächst, ob das zusammenzuführende Attribut eine Funktion ist. Wenn ja, prüfen Sie dann, ob das zu überschreibende Oberklassenattribut ebenfalls eine Funktion ist. Wenn beide Prüfungen zutreffen, müssen Sie eine _super-Methode für diese Eigenschaft vorbereiten.
Beachten Sie, dass hier ein anonymer Abschluss (der ein Funktionsobjekt zurückgibt) erstellt wird, um die hinzugefügte Supermethode zu kapseln. Aufgrund der Notwendigkeit, die laufende Umgebung aufrechtzuerhalten, sollten wir die alte Datei this._super (unabhängig davon, ob sie vorhanden ist oder nicht) speichern, um sie nach der Ausführung der Funktion zurückzusetzen. Dies hilft in Fällen, in denen es denselben Namen gibt (nicht gewünscht). versehentlich den Objektzeiger verlieren) Unvorhersehbare Probleme.
Erstellen Sie dann eine neue _super-Methode, die nur auf die überschriebene Methode in der Superklasse verweist. Gott sei Dank besteht keine Notwendigkeit, Änderungen an _super vorzunehmen oder den Bereich zu ändern, da sich die Ausführungsumgebung der Funktion automatisch mit dem Funktionsaufrufobjekt ändert (der Zeiger zeigt auf die Superklasse).
Zum Schluss wird die Methode des Literalobjekts aufgerufen. This._super() kann während der Ausführung der Methode verwendet werden. Nach der Ausführung der Methode wird das Attribut _super auf seinen ursprünglichen Zustand zurückgesetzt und die Funktion wird anschließend beendet.
Es gibt viele Möglichkeiten, den gleichen Effekt zu erzielen (ich habe schon einmal gesehen, wie super an sich selbst gebunden wird und dann mit arguments.callee darauf zugegriffen wird), aber ich bin der Meinung, dass diese Methode die Eigenschaften von Benutzerfreundlichkeit und Einfachheit am besten widerspiegelt.
Unter den vielen auf JavaScript-Prototypen basierenden Arbeiten, die ich abgeschlossen habe, ist dies der einzige Implementierungsplan für die Klassenvererbung, den ich veröffentlicht habe, um ihn mit Ihnen zu teilen. Ich denke, dass prägnanter Code (einfach zu erlernen, leicht zu vererben, weniger Downloads) zur Diskussion gestellt werden muss. Daher ist dieser Implementierungsplan für Leute, die den Aufbau und die Vererbung von JavaScript-Klassen lernen, ein guter Anfang.