Hallo Habr!
Bis heute habe ich meiner Meinung nach nur interessante Artikel englischsprachiger Autoren übersetzt. Und jetzt ist es Zeit, selbst etwas zu schreiben. Für den ersten Artikel habe ich ein Thema ausgewählt, das sicher für Nachwuchsentwickler nützlich sein wird, die in die "Mitte" wachsen wollen, weil Es wird die Ähnlichkeit / den Unterschied zwischen JavaScript und klassischen Programmiersprachen (C ++, C #, Java) in Bezug auf OOP analysieren. Also fangen wir an!
Die allgemeinen Bestimmungen des Paradigmas
Wenn wir uns die Definition von JavaScript in
Wikipedia ansehen, sehen wir das folgende Konzept:
JavaScript (/ ˈdʒɑːvːˌskrɪpt /; abbr. JS /ˈdʒeɪ.ɛs./) ist eine Programmiersprache mit mehreren Paradigmen. Unterstützt objektorientierte, imperative und funktionale Stile. Es ist eine Implementierung der ECMAScript-Sprache (ECMA-262-Standard).
Wie aus dieser Definition hervorgeht, existiert JavaScript nicht für sich, sondern ist eine Implementierung einiger EcmaScript-Spezifikationen. Darüber hinaus implementieren andere Sprachen diese Spezifikation.
Die folgenden Paradigmen sind in EcmaScript (im Folgenden: ES) vorhanden:
- strukturell
- OOP
- funktional
- zwingend erforderlich
- aspektorientiert (in seltenen Fällen)
OOP in ES wird in einer
Prototyp-Organisation implementiert. Von unerfahrenen Entwicklern als Antwort auf die Frage: "Wie unterscheidet sich OOP in JS von OOP in klassischen Sprachen?" In der Regel werden sie sehr vage: „Klassen sind in klassischen Sprachen und Prototypen in JS“.
In Wirklichkeit ist die Situation etwas komplizierter. In Bezug auf das Verhalten ist der Unterschied zwischen der
Dynamic Class Organization und der
Prototype Organization gering (es existiert zwar, ist aber nicht so global).
Schauen Sie sich Python oder Ruby an. In diesen Sprachen basiert OOP auf einer dynamischen Klassenorganisation. In beiden Sprachen können wir die Klasse eines Objekts im Verlauf des Programms dynamisch ändern, und Änderungen innerhalb der Klasse wirken sich auch dynamisch auf die von ihr generierten Entitäten aus. Genau wie in JS, aber in JS basiert OOP auf Prototypen.
Ein signifikanter Unterschied zwischen Sprachen mit einer statischen Klassenorganisation und einer Prototyporganisation. Der Unterschied an sich ist: „Es gibt Klassen. hier Prototypen “sind nicht so bedeutend.
Worauf basiert die statische Klassenorganisation?
Grundlage dieser Art von OOP sind die Konzepte „Klasse“ und „Essenz“.
Eine Klasse ist eine bestimmte formalisierte verallgemeinerte Menge von Merkmalen von Entitäten, die sie generieren kann. Das heißt, Dies ist ein bestimmter allgemeiner Plan aller von ihm erzeugten Objekte.
Es gibt zwei Arten von Merkmalen. Eigenschaften (Beschreibung einer Entität) und Methoden (Aktivität einer Entität, deren Verhalten).
Von einer Klasse generierte
Entitäten sind Kopien dieser Klasse, jedoch mit initialisierten Eigenschaften. Wie wir sehen, regelt die Klasse streng die Beschreibung einer Entität (Bereitstellung eines genau definierten Satzes von Eigenschaften) und ihr Verhalten (Bereitstellung einer streng definierten Liste von Methoden).
Hier ist ein kleines Beispiel für JAVA:
class Person{ String name;
Erstellen Sie nun eine Instanz der Klasse:
public class Program{ public static void main(String[] args) { Person tom; } }
Unsere
Tom- Entität hat alle Eigenschaften der
Person- Klasse, sie hat auch alle Methoden ihrer Klasse.
Das OOP-Paradigma bietet eine sehr breite Palette von Möglichkeiten zur Wiederverwendung von Code. Eine dieser Funktionen ist die
Vererbung .
Eine Klasse kann eine andere Klasse erweitern, wodurch eine Generalisierungs-Spezialisierungs-Beziehung erstellt wird. In diesem Fall werden die Eigenschaften der allgemeinen Klasse (Oberklasse) beim Erstellen in die Essenz der untergeordneten Klasse kopiert, und die Methoden stehen als Referenz zur Verfügung (entsprechend der hierarchischen Vererbungskette). Bei der statischen Klassentypisierung ist diese Kette
statisch , und bei der dynamischen Typisierung kann sie sich während der Programmausführung ändern. Dies ist der wichtigste Unterschied. Ich rate Ihnen jetzt, sich an diesen Moment zu erinnern. Wenn wir zur Prototyp-Organisation gelangen, wird das Wesentliche des Problems der Antwort „Es gibt Klassen, es gibt Prototypen“ offensichtlich.
Was sind die Nachteile dieses Ansatzes?
Ich denke, es ist offensichtlich, dass:
- Im Wesentlichen kann es Eigenschaften geben, die niemals nützlich sein werden.
- Eine Klasse kann Eigenschaften und Methoden, die sie generierten Entitäten bereitstellt, nicht dynamisch ändern, hinzufügen, löschen, d. H. kann seine Unterschrift nicht ändern.
- Im Wesentlichen können Eigenschaften oder Methoden, die in der Klasse des Elternteils (oder der hierarchischen Kette von Eltern) nicht vorhanden sind, nicht vorhanden sein
- Der Speicherverbrauch ist proportional zur Anzahl der Links in der Vererbungshierarchie (aufgrund von Kopiereigenschaften).
Worauf basiert die Prototypenorganisation?
Das Schlüsselkonzept der Prototyporganisation ist ein dynamisch veränderbares Objekt (dmo). DMO benötigt keine Klasse. Er selbst kann alle seine Eigenschaften und Methoden speichern.
Beim Festlegen eines DMO einer Eigenschaft wird geprüft, ob diese Eigenschaft darin vorhanden ist. Wenn es eine Eigenschaft gibt, wird sie einfach zugewiesen. Wenn dies nicht der Fall ist, wird die Eigenschaft hinzugefügt und mit dem übergebenen Wert initialisiert. DMOs können ihre Signatur während des Programms beliebig oft ändern.
Hier ist ein Beispiel:
Ich denke, jeder in diesem Fach weiß, dass die Klassensyntax in ES6 erschienen ist, aber dies ist nichts weiter als syntaktischer Zucker, d. H. Prototypen unter der Haube. Der obige Code sollte nicht als gute Codierungspraxis angesehen werden. Dies ist nichts weiter als eine Illustration. Sie wird in dieser Form dargestellt (jetzt verwenden alle normalen Menschen ES6-Klassen), um den Leser nicht zu verwirren und den Unterschied in den theoretischen Konzepten hervorzuheben.
Wenn wir das Tom-Objekt an die Konsole ausgeben, sehen wir, dass das Objekt selbst nur den _proto_-Link enthält, der standardmäßig immer vorhanden ist. Die Referenz verweist auf das Objekt Person, das ein Prototyp des Tom-Objekts ist.
Ein Prototyp ist ein Objekt, das als Prototyp für andere Objekte oder als Objekt dient, in das ein anderes Objekt bei Bedarf Eigenschaften und Methoden zeichnen kann.
Der Prototyp für ein Objekt kann ein beliebiges Objekt sein. Darüber hinaus kann ein Objekt seinen Prototyp während des Programms neu zuweisen.
Kommen wir zurück zu unserem Tom:
Tom.name = 'Tom';
Beachten Sie, dass der Name, die Alterseigenschaften und die sayHi-Methode die Eigenschaften des tomSon-Objekts sind. Gleichzeitig rufen wir die sayHi-Prototypmethode in tomSon sayHi explizit so auf, als wäre sie im Tom-Objekt, aber tatsächlich ist sie nicht vorhanden und wird implizit vom Person-Prototyp zurückgegeben. Wir arbeiten auch explizit mit dem Namen der Prototyp-Eigenschaft und erhalten implizit get die Nachnamen-Eigenschaft, die wir als unsere eigene Eigenschaft des tomSon-Objekts bezeichnen, aber tatsächlich nicht vorhanden ist. Die Nachnameigenschaft wird implizit über den Link
__proto__ vom Prototyp
abgerufen .
Wir setzen die Entwicklung der Geschichte unseres Tom und seines Sohnes John fort.
Bitte beachten Sie, dass wir während des Programms den Prototyp des bereits erstellten Objekts geändert haben. Dies ist die Ähnlichkeit der
Prototyp-Organisation und der
dynamischen Klassenorganisation . Deshalb lautet die Antwort "Es gibt Klassen, es gibt Prototypen" auf die Frage "Was ist der Unterschied zwischen klassischen Sprachen und JavaScript?" nicht ganz korrekt und weist auf ein Missverständnis der OOP-Theorie und ihrer Implementierung in Klassen und / oder Prototypen hin.
Bei der Prototyp-Organisation haben wir im Gegensatz zur statischen Klassenorganisation die Möglichkeit, Änderungen am Prototyp vorzunehmen, nachdem eine Entität erstellt wurde, die die Eigenschaften dieses Prototyps erbt. Diese Änderungen wirken sich auf die bereits erstellte Entität aus.
Ben.hobbies = ['chess', 'badminton'];
Dies wird als
Prototyporganisation bezeichnet, die die Modell- oder
Prototypvererbung delegiert .
Wie wird die Fähigkeit einer Entität bestimmt, ein bestimmtes Verhalten zu implementieren?
In einer statischen Klassenorganisation umfasst diese Operation das Überprüfen der Entität auf Mitgliedschaft in einer bestimmten Klasse, die das erforderliche Verhalten implementiert. In der Prototypenorganisation gibt es das Konzept der
Ententypisierung . Im Fall der Ententypisierung bedeutet das Überprüfen der Entität auf die Fähigkeit, ein bestimmtes Verhalten zu implementieren, das direkte Testen der Entität auf die Fähigkeit, dieses Verhalten zu einem bestimmten Zeitpunkt zu implementieren, d. H. In verschiedenen Teilen des Programms kann das Ergebnis der Überprüfung diametral entgegengesetzt sein.
Was sind die Vorteile eines Prototypansatzes?
- Mehr Flexibilität
- Entitäten haben keine Eigenschaften, die sie nicht benötigen.
Was sind die Nachteile?
- Weniger klar
- Es ist nicht immer einfach zu verfolgen, was als Ausgangspunkt für das unerwünschte Verhalten des Unternehmens diente, d. H. Der Prototyp ist weniger vorhersehbar als die statische Klassenorganisation
- Die Softwareentwickler sind trotz der Popularität und Verbreitung von JavaScript nicht ausreichend damit vertraut
Fazit
Damit werden wir heute enden. Ich hoffe, dass ich die Idee vermitteln konnte, dass der Unterschied zwischen klassischen Sprachen und JavaScript nicht mit dem Vorhandensein / Fehlen von Klassen und dem Vorhandensein / Fehlen von Prototypen zusammenhängt, sondern mit der statischen / dynamischen Natur der Organisation.
Natürlich wurde viel nicht berücksichtigt. Ich möchte keine Artikel schreiben, die zu lang sind, daher werden die Funktionen des Cascade-Modells in der Prototyp-Organisation und in den OOP-Tools (Polymorphismus, Kapselung, Abstraktion usw.) in den folgenden Artikeln erläutert.