Was ist der Prototyp?
Der Funktionstyp hat einen Eigenschaftsprototyp, der direkt in den Prototyp übersetzt wird. Diese Eigenschaft ist ein Zeiger, der auf ein Objekt zeigt, das einige Eigenschaften und Methoden enthält, die von allen Instanzen (Objekten) gemeinsam genutzt werden, die durch die aktuelle Funktion generiert werden.
Basierend auf dem, was zuvor gesagt wurde, können Sie den folgenden Code erhalten:
Funktion Person () {...} Person.Prototype = {Land: 'China', SayName: function () {...}}Zunächst wird eine Instanzperson des Funktionstyps erstellt, und dann ist der Methodenprototyp der Person ein Objekt, und die Deklaration zeigt auf ein Objekt. Die Eigenschaften und Methoden in diesem Objekt werden durch die von der aktuelle Person erzeugte Instanz geteilt. Das heißt:
Person1 = neuer Person (); Person2 = neue Person ();
Person1 und Person2 werden beide durch Instanzen des Personentyps wieder generiert. Beide haben das gemeinsame Eigentumsland und die Methode SayName, weil sie alle einen Zeiger (__Proto__) haben, der direkt auf das von Person hingewiesene Objekt hinweist. Bitte beachten Sie jedoch, dass der __Proto__ -Zeiger nicht Standard ist. Es wird nur von Browsern wie Chrom und Firefox definiert. Tatsächlich wird diese Eigenschaft nicht verwendet, sondern wird nur als Verständnis des Prototyps verwendet:
In Bezug auf die Verwendung von Prototypen und anderen Methoden werden wir später später darüber sprechen.
Erstellen Sie ein Objektmuster
Schauen wir uns als nächstes die Methoden und gemeinsamen Muster zum Erstellen von Objekten sowie deren Vor- und Nachteile an.
1. Fabrikmodell
Genau wie bei einer Fabrik wird der Prozess des Erstellens konkreter Objekte abstrahiert und Funktionen werden verwendet, um die Details des Erstellens von Objekten mit bestimmten Schnittstellen zu verkörpern. Durch die Verwendung von Funktionen anstelle einer teilweisen Wiederholungsarbeit lautet der Code wie folgt:
Funktion CreatePerson (Name, Alter, Job) {var o = new Object (); O.Name = Name; O.age = Alter; O.Job = Job; o.sayname = function () {alert (this.name); }; return o;} var person1 = CreatePerson ("jiangshui", "22", "Ingenieur");Dies erzeugt eine Person, und das Fabrikmuster löst das Problem der wiederholten Erstellung mehrerer ähnlicher Objekte, löst jedoch das Problem der Objekterkennung nicht. Es ist nur so, dass ein Objekt einfach erstellt wird und egal ob dieses Objekt aus einer menschlichen Vorlage oder einer tierischen Vorlage erstellt wird, es ist unmöglich, den Typ dieses Objekts zu unterscheiden.
2. Konstruktormodus
Erstellen Sie einen benutzerdefinierten Konstruktor, um Eigenschaften und Methoden benutzerdefinierter Objekttypen zu definieren.
Funktionsperson (Name, Alter, Job) {this.name = name; this.age = Alter; this.job = jpb; this.sayname = function () {alert (this.name); };}; var person1 = new Person (...);3. Der Unterschied zwischen dem Konstruktormodus und dem Werksmodus:
Person ist ein Objekt des Funktionstyps. Nach dem Neuen wird weiterhin ein Objekt generiert. Da dieses neu generierte Objekt jedoch in der Funktion übergeben und diesem Zeiger zugewiesen wird, wird der in übergebene Inhalt zur Eigenschaft oder Methode des neu generierten Objekts.
Die Standardgewohnheit der Konstruktoren wird im ersten Brief aktiviert. Die obige Codeausführung führt die folgenden Schritte durch:
In den auf diese Weise generierten Fällen enthalten sie alle standardmäßig ein Konstruktorattribut, das beispielsweise auf die Konstruktorfunktion hinweist:
alert (Person1.Constructor == Person);
Daher gibt es unter Verwendung des Konstruktor -Musters eine Art Unterscheidung und seine Instanz kann als spezifischer Typ identifiziert werden.
Darüber hinaus ist der Konstruktor eine gewöhnliche Funktion. Da Sie ein Feedback haben möchten, um ein neues Objekt zu erhalten, verwenden Sie neue, um es anzurufen. Wenn nicht, ist die direkte Ausführung wie eine normale Funktion. Wenn Sie beispielsweise Person ausführen.
Der Konstruktormodus ist ebenfalls fehlerhaft. Die Methoden im Konstruktormodus werden in jeder Instanz nachgebildet, sodass die gleichnamigen Funktionen in verschiedenen Instanzen nicht gleich sind. Zum Beispiel:
Person1.SayName == Person2.SayName; //FALSCH
Das heißt, jede Objektinstanz, Attribute und Methoden, die vom Konstruktor generiert werden, sind eindeutig und werden kopiert. Attribute sind eindeutig, da dies genau der Unterschied zwischen Objekten ist, aber viele Methoden haben die gleichen Funktionen und den gleichen Code. Wenn Sie sie wiederholt kopieren, werden Sie offensichtlich Ressourcen verschwenden.
So können wir die Funktion nach außen platzieren und dann mit einem Zeiger im Konstruktor auf die Funktion verweisen. In der generierten Instanz speichert die Methode einen Zeiger auf eine bestimmte Funktion, was eine gemeinsame Funktion bedeutet:
Funktionsperson (Name, Alter) {this.name = name; this.age = Alter; this.sayname = sayname;} Funktion Sayname () {alert (this.name);}Auf diese Weise wird diese Funktion jedoch zu einer globalen Funktion und ist nicht stark mit dem Personkonstruktor korreliert und hat keine Einkapselung.
Kommen Sie als nächstes zum Prototypenmodus.
Prototypmodus
Ein Teil der Grundlagen zu Prototypen wurde früher eingeführt. Einfach ausgedrückt, jede Funktion hat ein Prototypattribut, das auf ein Objekt (Prototyp -Objekt) zeigt, und einige Eigenschaften oder Methoden können in dieses Objekt platziert werden. Anschließend hat die durch diese Funktion generierte Instanz ein unregelmäßiges Attribut (__Proto__), das auf den Prototyp hinweist.
Aus dieser Sicht sollten Sie in der Lage sein zu verstehen, dass die von Prototypen generierten Eigenschaften und Methoden von allen Instanzen geteilt werden.
Dies löst nur das Problem, Funktionen im obigen Konstruktormodus und in den Beispielen zu teilen. Zum Beispiel der folgende Code:
Funktion person () {....} Person.Prototype.name = "jiangShui"; Person.Prototype.SayName = function () {alert (this.name);}; var person1 = new Person (); Person1.SayName (); // Jiangshuioder
Person.Prototype = {Konstruktor: Person, Name: "JiangShui", sayName: function () {alert (this.name); }};Die zweite Methode deckt das gesamte Prototyp -Objekt ab, sodass Sie die Konstruktoreigenschaft manuell angeben müssen, wobei Sie auf die Konstruktorfunktion verweisen, sonst verweist sie auf das Objekt.
Lassen Sie uns ihre Beziehung aufklären:
Verwenden Sie IsPrototypeof (), um die Beziehung zwischen Objekten zu bestimmen. Zum Beispiel:
Person.Prototype.iPrototypeof (Person1);
Wenn der Code eine bestimmte Eigenschaft eines Objekts liest, wird eine Suche durchgeführt. Beginnen Sie mit dem aktuellen Objekt und suchen Sie, wenn nicht, nach dem vom Zeiger gezeigten Prototyp -Objekt, ohne nach dem Konstruktor zu suchen. Die Objektinstanz kann zugegriffen werden, kann jedoch den Wert des Prototypobjekts nicht überschreiben. Wenn in der Instanz ein Attribut mit demselben Namen wie das Prototypobjekt festgelegt ist, endet der Suchprozess in der Instanz, ohne auf das Prototyp -Objekt zuzugreifen, sodass der Zweck des Überschreibens erreicht wird. Selbst wenn diese Eigenschaft auf NULL eingestellt ist, bedeutet dies, dass die Eigenschaft bereits in der Instanz vorhanden ist und die Eigenschaft nicht storniert wird, sodass auf die entsprechende Eigenschaft des Prototyps zugegriffen werden kann.
Daher müssen Sie den Löschbetreiber verwenden, um die Instanzattribute vollständig zu löschen, damit der Prototyp erneut besucht werden kann.
Der Prototyp ist dynamisch, und alle Modifikationen am Prototypobjekt können sofort aus der Instanz reflektiert werden. Der Grund ist die lose Verbindungsbeziehung zwischen der Instanz und dem Prototyp. Jedes Mal, wenn die Eigenschaftsmethode der Instanz aufgerufen wird, wird eine Abfrage durchgeführt. Wenn sich der Prototyp ändert, ändert sich auch das Abfrageergebnis.
Nach dem Verständnis des Prototyps können wir dem nativen Objekt auch neue Methoden oder Attribute hinzufügen. Native Referenztypen wie Objekt, Array, String usw. ähneln den oben genannten Konstruktoren. Wir können Prototypen verwenden, um ihre Methoden zu erweitern. Zum Beispiel:
String.Prototype.Startswith = Funktion (Text) {return this.indexof (text) == 0;}; var msg = "Hallo Welt"; msg.startswith ("Hallo");Dieser Code fügt der nationalen Referenztyp -Zeichenfolge eine Start -With -Methode hinzu, die in einem Parameter übergeben wird, um festzustellen, ob der zu testende String mit einem Parameter beginnt. Aufgrund der dynamischen Natur des Prototyps erhalten alle Variablen des String -Typs diese Methode durch Ausführen.
Diese Methode wird jedoch nicht empfohlen. Wenn Sie zu viel und zu viel Code verwenden, wird dies zu Wartungsschwierigkeiten, Verwirrung im Code usw. führen. Im Allgemeinen wird zuerst ein nationaler Referenztyp vererbt und dann auf einem neu angepassten Typ erstellt. In Bezug auf die Erbschaft werden wir es später zusammenfassen.
Das Prototypmuster ist auch nicht allmächtig. Alle Attribute und Methoden im Prototyp werden von allen Instanzen gemeinsam genutzt, daher ist sie sehr für Funktionen und andere Funktionen geeignet, aber für Attribute, die Referenztypen enthalten, entstehen einige Konflikte. Zum Beispiel:
Funktion person () {} person.Prototype = {Konstruktor: Person, Freunde: ["Greg", "Jack"]}; var person1 = new Person (); var person2 = new Person (); person1.friends.push ("tom"); console.log (person2.friends);Sie werden in der Konsole sehen, dass es einen zusätzlichen Tom für Person2 -Freunde gibt, das nicht das ist, was ich will, sondern wenn er seinen Freund zu Person definiert hat, wirkt sich die Instanzperson aus 2 aus.
Daher müssen wir es in Kombination mit dem Prototypmuster und dem Konstruktormuster verwenden.
Verwenden Sie den Konstruktormodus und den Prototypenmodus in Kombination
Dies ist das am häufigsten verwendete Muster. Der Konstruktor wird verwendet, um Instanzeigenschaften zu definieren und durch Übergabe von Parametern anzupassen. Der Prototyp wird verwendet, um Methoden oder Attribute zu definieren, die die gemeinsame Nutzung aller Instanzen erfordern. Auf diese Weise wird die Anpassung erreicht, das Teilen wird sichergestellt und Probleme werden vermieden.
Funktionsperson (Name, Alter, Job) {this.name = name; this.age = Alter; this.job = Job; this.friends = ["Greg", "Jack"];} Person.Prototype = {Konstruktor: Person, SayName: function () {alert (this.name); }}; var jiangshui = new Person ("jiangshui", "22", "Ingenieur");Praktische Anwendungsbeispiele
OK, hier verstehen Sie vielleicht, was der Prototyp ist und wie Objekte erstellt werden können, aber was sind die Verwendungen von diesen? In der Tat war meine frühere Arbeit nur, einen Code mit JQuery zu schreiben, und ich konnte keine Kapselung verwenden und dann Objekte zum Implementieren von Funktionen usw. generieren.
Diese Entwicklungsmethode wird hauptsächlich für die modulare und montierende Entwicklung verwendet. Zum Beispiel die Popup-Funktion, die Sie häufig verwenden, können Sie den Popup-Code natürlich jedes Mal einfügen und kopieren und dann ändern und im Projekt verwenden. Eine bessere Wahl besteht darin, Ihren Popup-Funktionscode abstrakt in eine solche Komponente zu verkapulieren, damit Sie, wenn Sie Popups verwenden müssen, nur Parameter übergeben müssen, um eine Popup-Instanz zu generieren, und Sie können sie aufrufen.
Prototypobjekte und Prototypketten
In JavaScript ist alles ein Objekt, aber es gibt auch Unterschiede in Objekten. Es kann grob in zwei Kategorien unterteilt werden, nämlich: gewöhnliche Objekte (Objekt) und Funktionsobjekte (Funktion).
Im Allgemeinen sind die durch die neuen Funktion generierten Objekte Funktionsobjekte und andere Objekte gewöhnliche Objekte.
Geben Sie ein Beispiel:
Funktion f1 () {// todo} var f2 = function () {// Todo}; var f3 = neue Funktion ('x', 'console.log (x)'); var O1 = {}; var o2 = neues Objekt (); var o3 = neu f1 (); console.log( typeof f1,//function typeof f2,//function typeof f3,//function typeof o1,//object typeof o2,//object typeof o3 //object);>> function function object object object objectF1 gehört zur Erklärung einer Funktion. Die häufigste Art, eine Funktion zu definieren, besteht darin, dass F2 tatsächlich eine anonyme Funktion ist. Weisen Sie diese anonyme Funktion F2 zu, die zu einem Funktionsausdruck gehört. F3 ist nicht häufig, aber es ist auch ein Funktionsobjekt.
Funktion ist ein Objekt, das mit JS geliefert wird. Wenn F1 und F2 erstellt werden, erstellt JS diese Objekte automatisch über neue Funktionen (). Daher werden diese drei Objekte durch neue Funktion () erstellt.
Es gibt zwei Möglichkeiten, Objekte in JavaScript zu erstellen: Objektliterale und neue Ausdrücke. Die Schaffung von O1 und O2 entspricht diesen beiden Arten nur. Konzentrieren wir uns auf O3. Wenn Sie die Ideen von Java und C# zum Verständnis verwenden, ist O3 ein Instanzobjekt von F1, und O3 und F1 sind der gleiche Typ. Zumindest dachte ich schon mal, aber es ist nicht ...
Wie verstehst du es? Es ist sehr einfach. Sehen Sie, ob O3 durch neue Funktion erzeugt wird. Offensichtlich nicht. Da es sich nicht um ein Funktionsobjekt handelt, handelt es sich um ein gewöhnliches Objekt.
Lassen Sie uns nach einem einfachen Verständnis von Funktionsobjekten und gewöhnlichen Objekten über Prototypen und Prototyp -Ketten in JavaScript erfahren:
In JS werden bei JS, wenn ein Funktionsobjekt F1 erstellt wird, einige Eigenschaften in das Objekt integriert, einschließlich Prototyp und __Proto__, Prototyp ist das Prototyp -Objekt, das einige Eigenschaften und Methoden von F1 aufzeichnet.
Es ist zu beachten, dass der Prototyp für F1 unsichtbar ist, dh F1 sucht nicht nach Eigenschaften und Methoden im Prototyp.
Funktion f () {} f.Prototype.foo = "abc"; console.log (f.foo); //undefiniertWie nutzt der Prototyp? Tatsächlich ist die Hauptfunktion des Prototyps die Vererbung. In den Laien -Begriffen sind die im Prototyp definierten Eigenschaften und Methoden ihren "Nachkommen" überlassen, sodass Unterklassen vollständig auf die Eigenschaften und Methoden im Prototyp zugreifen können.
Um zu wissen, wie F1 Prototypen für "Nachkommen" verlässt, müssen wir die Prototypkette in JS verstehen. Zu diesem Zeitpunkt ist der __Proto__ in JS in den Markt eingetreten. Dieser Typ ist sehr seltsam und verborgen, so dass Sie ihn oft nicht sehen, aber er existiert sowohl in gewöhnlichen Objekten als auch in Funktionsobjekten. Seine Funktion besteht darin, das Prototypobjekt der übergeordneten Klasse zu speichern. Wenn JS durch einen neuen Ausdruck ein Objekt erstellt, weist es normalerweise den Prototyp der übergeordneten Klasse dem __proproto__ -Attribut des neuen Objekts zu, das eine Generation von Vererbung bildet ...
Funktion f () {} f.Prototype.foo = "abc"; var obj = new f (); console.log (obj.foo); //ABCJetzt wissen wir, dass __Proto__ in OBJ den Prototyp von F rettet. Was wird also in __Proto__ in Fs Prototyp gespeichert? Siehe das folgende Bild:
Wie in der Abbildung gezeigt, ist das Objekt. Wie in der Abbildung unten gezeigt:
Nachdem das OBJ -Objekt über eine solche Prototypkette verfügt, wird OBJ zuerst feststellen, ob es das Attribut hat, aber keinen eigenen Prototyp findet. Wenn Foo nicht gefunden werden kann, wird OBJ nacheinander die Prototypkette suchen ...
Im obigen Beispiel definieren wir das FOO -Attribut auf dem Prototyp von F, und dann findet OBJ dieses Attribut auf der Prototypkette und führt es aus.