Auf der Seite API-Referenzbereich heißt es:
Ein Bereich kann von einem übergeordneten Bereich erben.
Auf der Seite zum Umfang des Entwicklerhandbuchs heißt es:
Ein Bereich (prototypisch) erbt Eigenschaften von seinem übergeordneten Bereich.
- Erbt ein untergeordneter Bereich immer prototypisch von seinem übergeordneten Bereich?
- Gibt es Ausnahmen?
- Wenn es erbt, ist es immer eine normale prototypische JavaScript-Vererbung?
javascript
angularjs
inheritance
prototype
prototypal-inheritance
Mark Rajcok
quelle
quelle
Antworten:
Schnelle Antwort :
Ein untergeordneter Bereich erbt normalerweise prototypisch von seinem übergeordneten Bereich, jedoch nicht immer. Eine Ausnahme von dieser Regel ist eine Direktive mit
scope: { ... }
- dies schafft einen "isolierten" Bereich, der nicht prototypisch erbt. Dieses Konstrukt wird häufig beim Erstellen einer Direktive für "wiederverwendbare Komponenten" verwendet.Was die Nuancen betrifft, ist die Bereichsvererbung normalerweise unkompliziert ... bis Sie im untergeordneten Bereich eine bidirektionale Datenbindung (dh Formularelemente, ng-Modell) benötigen . Ng-repeat, ng-switch und ng-include können Sie auslösen, wenn Sie versuchen, innerhalb des untergeordneten Bereichs an ein Grundelement (z. B. Zahl, Zeichenfolge, Boolescher Wert) im übergeordneten Bereich zu binden . Es funktioniert nicht so, wie die meisten Leute erwarten, dass es funktionieren sollte. Der untergeordnete Bereich erhält eine eigene Eigenschaft, die die übergeordnete Eigenschaft mit demselben Namen verbirgt / schattiert. Ihre Problemumgehungen sind
New AngularJS Entwickler oft nicht bewusst , dass
ng-repeat
,ng-switch
,ng-view
,ng-include
undng-if
alle neuen untergeordneten Bereiche schaffen, so oft das Problem zeigt sich , wenn diese Richtlinien beteiligt sind. (In diesem Beispiel wird das Problem kurz veranschaulicht.)Dieses Problem mit Grundelementen kann leicht vermieden werden, indem die "Best Practice" befolgt wird, immer ein 'zu haben.' in deinen ng-modellen - schau dir 3 minuten an. Misko demonstriert das primitive Bindungsproblem mit
ng-switch
.Ein ... haben '.' In Ihren Modellen wird sichergestellt, dass die prototypische Vererbung im Spiel ist. Also, benutze
Lange Antwort :
Prototypische Vererbung von JavaScript
Ebenfalls im AngularJS-Wiki platziert: https://github.com/angular/angular.js/wiki/Understanding-Scopes
Es ist wichtig, zunächst ein solides Verständnis der prototypischen Vererbung zu haben, insbesondere wenn Sie aus einem serverseitigen Hintergrund stammen und mit der klassischen Vererbung besser vertraut sind. Lassen Sie uns das zuerst überprüfen.
Angenommen, parentScope hat die Eigenschaften aString, aNumber, anArray, anObject und aFunction. Wenn childScope prototypisch von parentScope erbt, haben wir:
(Um Platz zu sparen, zeige ich das
anArray
Objekt als einzelnes blaues Objekt mit seinen drei Werten und nicht als einzelnes blaues Objekt mit drei separaten grauen Literalen.)Wenn wir versuchen, vom untergeordneten Bereich aus auf eine im parentScope definierte Eigenschaft zuzugreifen, sucht JavaScript zuerst im untergeordneten Bereich, nicht in der Eigenschaft, dann im geerbten Bereich und sucht die Eigenschaft. (Wenn die Eigenschaft im parentScope nicht gefunden wurde, wird die Prototypenkette bis zum Stammbereich fortgesetzt.) Das ist also alles wahr:
Angenommen, wir machen dann Folgendes:
Die Prototypenkette wird nicht konsultiert, und dem childScope wird eine neue aString-Eigenschaft hinzugefügt. Diese neue Eigenschaft verbirgt / schattiert die gleichnamige parentScope-Eigenschaft. Dies wird sehr wichtig, wenn wir unten ng-repeat und ng-include diskutieren.
Angenommen, wir machen dann Folgendes:
Die Prototypenkette wird konsultiert, da die Objekte (anArray und anObject) nicht im childScope gefunden werden. Die Objekte befinden sich im parentScope und die Eigenschaftswerte werden für die ursprünglichen Objekte aktualisiert. Dem childScope werden keine neuen Eigenschaften hinzugefügt. Es werden keine neuen Objekte erstellt. (Beachten Sie, dass in JavaScript Arrays und Funktionen auch Objekte sind.)
Angenommen, wir machen dann Folgendes:
Die Prototypenkette wird nicht konsultiert, und der untergeordnete Bereich erhält zwei neue Objekteigenschaften, die die gleichnamigen parentScope-Objekteigenschaften ausblenden / schattieren.
Imbissbuden:
Ein letztes Szenario:
Wir haben zuerst die childScope-Eigenschaft gelöscht. Wenn wir dann erneut versuchen, auf die Eigenschaft zuzugreifen, wird die Prototypenkette konsultiert.
Vererbung des Winkelbereichs
Die Anwärter:
scope: true
, Direktive mittransclude: true
.scope: { ... }
. Dadurch wird stattdessen ein "isolierter" Bereich erstellt.Beachten Sie, dass Direktiven standardmäßig keinen neuen Bereich erstellen, dh der Standardwert ist
scope: false
.ng-include
Angenommen, wir haben in unserem Controller:
Und in unserem HTML:
Jedes ng-include generiert einen neuen untergeordneten Bereich, der prototypisch vom übergeordneten Bereich erbt.
Wenn Sie (z. B. "77") in das erste Eingabetextfeld eingeben, erhält der untergeordnete Bereich eine neue
myPrimitive
Bereichseigenschaft, die die gleichnamige übergeordnete Bereichseigenschaft verbirgt / schattiert. Dies ist wahrscheinlich nicht das, was Sie wollen / erwarten.Die Eingabe (z. B. "99") in das zweite Eingabetextfeld führt nicht zu einer neuen untergeordneten Eigenschaft. Da tpl2.html das Modell an eine Objekteigenschaft bindet, wird die prototypische Vererbung aktiviert, wenn das ngModel nach dem Objekt myObject sucht - es findet es im übergeordneten Bereich.
Wir können die erste Vorlage, die $ parent verwendet, neu schreiben, wenn wir unser Modell nicht von einem Grundelement in ein Objekt ändern möchten:
Das Eingeben (z. B. "22") in dieses Eingabetextfeld führt nicht zu einer neuen untergeordneten Eigenschaft. Das Modell ist jetzt an eine Eigenschaft des übergeordneten Bereichs gebunden (da $ parent eine untergeordnete Bereichseigenschaft ist, die auf den übergeordneten Bereich verweist).
Für alle Bereiche (prototypisch oder nicht) verfolgt Angular immer eine Eltern-Kind-Beziehung (dh eine Hierarchie) über die Bereichseigenschaften $ parent, $$ childHead und $$ childTail. Normalerweise zeige ich diese Bereichseigenschaften nicht in den Diagrammen.
In Szenarien, in denen Formularelemente nicht beteiligt sind, besteht eine andere Lösung darin, eine Funktion im übergeordneten Bereich zu definieren, um das Grundelement zu ändern. Stellen Sie dann sicher, dass das untergeordnete Element immer diese Funktion aufruft, die dem untergeordneten Bereich aufgrund der prototypischen Vererbung zur Verfügung steht. Z.B,
Hier ist ein Beispiel für eine Geige , die diesen Ansatz der "übergeordneten Funktion" verwendet. (Die Geige wurde als Teil dieser Antwort geschrieben: https://stackoverflow.com/a/14104318/215945 .)
Siehe auch https://stackoverflow.com/a/13782671/215945 und https://github.com/angular/angular.js/issues/1267 .
ng-Schalter
Die Vererbung des ng-switch-Bereichs funktioniert genauso wie ng-include. Wenn Sie also eine bidirektionale Datenbindung an ein Grundelement im übergeordneten Bereich benötigen, verwenden Sie $ parent oder ändern Sie das Modell in ein Objekt und binden Sie es dann an eine Eigenschaft dieses Objekts. Dadurch wird vermieden, dass untergeordnete Bereiche die Eigenschaften des übergeordneten Bereichs ausblenden / schattieren.
Siehe auch AngularJS, Bindungsbereich eines Switch-Gehäuses?
ng-wiederholen
Ng-Repeat funktioniert etwas anders. Angenommen, wir haben in unserem Controller:
Und in unserem HTML:
Für jedes Element / jede Iteration erstellt ng-repeat einen neuen Bereich, der prototypisch vom übergeordneten Bereich erbt, aber den Wert des Elements auch einer neuen Eigenschaft im neuen untergeordneten Bereich zuweist . (Der Name der neuen Eigenschaft ist der Name der Schleifenvariablen.) Der Angular-Quellcode für ng-repeat lautet eigentlich wie folgt:
Wenn item ein Grundelement ist (wie in myArrayOfPrimitives), wird der neuen untergeordneten Bereichseigenschaft im Wesentlichen eine Kopie des Werts zugewiesen. Durch Ändern des Werts der Eigenschaft des untergeordneten Bereichs (dh unter Verwendung des ng-Modells, also des untergeordneten Bereichs
num
) wird das Array, auf das der übergeordnete Bereich verweist, nicht geändert. In der ersten ng-Wiederholung oben erhält jedernum
untergeordnete Bereich eine Eigenschaft, die vom Array myArrayOfPrimitives unabhängig ist:Diese ng-Wiederholung funktioniert nicht (wie Sie es wollen / erwarten). Durch Eingabe in die Textfelder werden die Werte in den grauen Feldern geändert, die nur in den untergeordneten Bereichen sichtbar sind. Wir möchten, dass sich die Eingaben auf das Array myArrayOfPrimitives auswirken, nicht auf eine primitive Eigenschaft des untergeordneten Bereichs. Um dies zu erreichen, müssen wir das Modell so ändern, dass es ein Array von Objekten ist.
Wenn das Element ein Objekt ist, wird der neuen untergeordneten Bereichseigenschaft ein Verweis auf das ursprüngliche Objekt (keine Kopie) zugewiesen. Durch Ändern des Werts der Eigenschaft des untergeordneten Bereichs (dh unter Verwendung von ng-model
obj.num
) wird das Objekt geändert, auf das der übergeordnete Bereich verweist. In der zweiten ng-Wiederholung oben haben wir also:(Ich habe eine Linie grau gefärbt, damit klar ist, wohin sie führt.)
Dies funktioniert wie erwartet. Durch Eingabe in die Textfelder werden die Werte in den grauen Feldern geändert, die sowohl für den untergeordneten als auch für den übergeordneten Bereich sichtbar sind.
Siehe auch Schwierigkeiten mit ng-Modell, ng-Wiederholung und Eingaben und https://stackoverflow.com/a/13782671/215945
ng-controller
Das Verschachteln von Controllern, die ng-controller verwenden, führt zu einer normalen prototypischen Vererbung, genau wie ng-include und ng-switch. Daher gelten dieselben Techniken. "Es wird jedoch als schlechte Form angesehen, wenn zwei Controller Informationen über die Vererbung von $ scope austauschen." - http://onehungrymind.com/angularjs-sticky-notes-pt-1-architecture/ Ein Dienst sollte zum Teilen von Daten zwischen verwendet werden Controller stattdessen.
(Wenn Sie Daten wirklich über die Vererbung des Controller-Bereichs freigeben möchten, müssen Sie nichts tun. Der untergeordnete Bereich hat Zugriff auf alle Eigenschaften des übergeordneten Bereichs. Siehe auch Die Ladereihenfolge des Controllers unterscheidet sich beim Laden oder Navigieren. )
Richtlinien
scope: false
) - Die Direktive erstellt keinen neuen Bereich, daher gibt es hier keine Vererbung. Dies ist einfach, aber auch gefährlich, da beispielsweise eine Direktive den Eindruck erweckt, dass eine neue Eigenschaft im Bereich erstellt wird, obwohl tatsächlich eine vorhandene Eigenschaft überlastet wird. Dies ist keine gute Wahl, um Anweisungen zu schreiben, die als wiederverwendbare Komponenten gedacht sind.scope: true
- Die Direktive erstellt einen neuen untergeordneten Bereich, der prototypisch vom übergeordneten Bereich erbt. Wenn mehr als eine Direktive (für dasselbe DOM-Element) einen neuen Bereich anfordert, wird nur ein neuer untergeordneter Bereich erstellt. Da wir eine "normale" prototypische Vererbung haben, ist dies wie ng-include und ng-switch. Seien Sie also vorsichtig bei der bidirektionalen Datenbindung an Grundelemente des übergeordneten Bereichs und beim Ausblenden / Abschatten der Eigenschaften des übergeordneten Bereichs durch den untergeordneten Bereich.scope: { ... }
- Die Richtlinie schafft einen neuen isolierten / isolierten Bereich. Es erbt nicht prototypisch. Dies ist normalerweise die beste Wahl, wenn Sie wiederverwendbare Komponenten erstellen, da die Direktive den übergeordneten Bereich nicht versehentlich lesen oder ändern kann. Solche Anweisungen benötigen jedoch häufig Zugriff auf einige übergeordnete Bereichseigenschaften. Der Objekt-Hash wird verwendet, um eine bidirektionale Bindung (mit '=') oder eine einseitige Bindung (mit '@') zwischen dem übergeordneten Bereich und dem isolierten Bereich einzurichten. Es gibt auch '&' zum Binden an übergeordnete Bereichsausdrücke. Diese alle erstellen also lokale Bereichseigenschaften, die vom übergeordneten Bereich abgeleitet sind. Beachten Sie, dass Attribute zum Einrichten der Bindung verwendet werden. Sie können nicht nur auf die Namen der übergeordneten Bereichseigenschaften im Objekt-Hash verweisen, sondern müssen ein Attribut verwenden. Dies funktioniert beispielsweise nicht, wenn Sie an die übergeordnete Eigenschaft binden möchtenparentProp
im isolierten Bereich:<div my-directive>
undscope: { localProp: '@parentProp' }
. Ein Attribut muss verwendet werden, um jede übergeordnete Eigenschaft anzugeben, an die die Direktive binden möchte:<div my-directive the-Parent-Prop=parentProp>
undscope: { localProp: '@theParentProp' }
.Isolieren Sie die
__proto__
Referenzen des Bereichs . $ Parent des isolierten Bereichs verweist auf den übergeordneten Bereich. Obwohl er isoliert ist und nicht prototypisch vom übergeordneten Bereich erbt, handelt es sich dennoch um einen untergeordneten Bereich.Für das Bild unten haben wir
<my-directive interpolated="{{parentProp1}}" twowayBinding="parentProp2">
und nehmenscope: { interpolatedProp: '@interpolated', twowayBindingProp: '=twowayBinding' }
außerdem an, dass die Direktive dies in ihrer Verknüpfungsfunktion tut:
scope.someIsolateProp = "I'm isolated"
Weitere Informationen zu isolierten Bereichen finden Sie unter http://onehungrymind.com/angularjs-sticky-notes-pt-2-isolated-scope/
transclude: true
- Die Richtlinie erstellt einen neuen "transkludierten" untergeordneten Bereich, der prototypisch vom übergeordneten Bereich erbt. Der transkludierte und der isolierte Bereich (falls vorhanden) sind Geschwister - die $ parent-Eigenschaft jedes Bereichs verweist auf denselben übergeordneten Bereich. Wenn sowohl ein transkludierter als auch ein isolierter Bereich vorhanden sind, verweist die isolierte Bereichseigenschaft $$ nextSibling auf den transkludierten Bereich. Mir sind keine Nuancen mit dem übertragenen Umfang bekannt.Nehmen Sie für das Bild unten dieselbe Direktive wie oben mit diesem Zusatz an:
transclude: true
Diese Geige hat eine
showScope()
Funktion, mit der ein isoliertes und ein ausgeschlossenes Zielfernrohr untersucht werden kann. Siehe die Anweisungen in den Kommentaren in der Geige.Zusammenfassung
Es gibt vier Arten von Bereichen:
scope: true
scope: {...}
. Dieser ist nicht prototypisch, aber '=', '@' und '&' bieten einen Mechanismus für den Zugriff auf Eigenschaften des übergeordneten Bereichs über Attribute.transclude: true
. Dies ist auch eine normale Vererbung des prototypischen Bereichs, aber es ist auch ein Geschwister eines jeden isolierten Bereichs.Für alle Bereiche (prototypisch oder nicht) verfolgt Angular immer eine Eltern-Kind-Beziehung (dh eine Hierarchie) über die Eigenschaften $ parent und $$ childHead und $$ childTail.
Diagramme wurden mit erstellt graphviz"* .dot" -Dateien, die sich auf Github befinden . Tim Caswells " Lernen von JavaScript mit Objektgraphen" war die Inspiration für die Verwendung von GraphViz für die Diagramme.
quelle
__proto__
Referenzobjekt des Bereichs isolieren." sollte stattdessen "Isolieren Sie die__proto__
Referenzen des Bereichs auf ein Bereichsobjekt" lauten . In den letzten beiden Bildern sollten die orangefarbenen "Objekt" -Felder stattdessen "Bereichs" -Felder sein.Ich möchte in keiner Weise mit Marks Antwort konkurrieren, sondern nur das Stück hervorheben, das schließlich alles zum Klicken brachte, als jemand, der neu in der Vererbung von Javascript und seiner Prototypenkette ist .
Nur Eigenschaftslesevorgänge durchsuchen die Prototypkette, keine Schreibvorgänge. Also, wenn Sie einstellen
Es sieht nicht die Kette nach, aber wenn Sie einstellen
Innerhalb dieser Schreiboperation wird ein subtiler Lesevorgang ausgeführt, bei dem versucht wird, myThing nachzuschlagen, bevor auf die Requisite geschrieben wird. Deshalb wird beim Schreiben in object.properties vom Kind auf die Objekte des Elternteils zugegriffen.
quelle
Ich möchte der Antwort von @Scott Driscoll ein Beispiel für eine prototypische Vererbung mit Javascript hinzufügen. Wir werden ein klassisches Vererbungsmuster mit Object.create () verwenden, das Teil der EcmaScript 5-Spezifikation ist.
Zuerst erstellen wir die "Parent" -Objektfunktion
Fügen Sie dann einen Prototyp zur Objektfunktion "Übergeordnet" hinzu
Erstellen Sie die Objektfunktion "Kind"
Untergeordneten Prototyp zuweisen (Kinderprototyp vom übergeordneten Prototyp erben lassen)
Weisen Sie den richtigen "Child" -Prototypkonstruktor zu
Fügen Sie einem untergeordneten Prototyp die Methode "changeProps" hinzu, die den "primitiven" Eigenschaftswert im untergeordneten Objekt neu schreibt und den Wert "object.one" sowohl im untergeordneten als auch im übergeordneten Objekt ändert
Initiieren Sie Objekte von Eltern (Vater) und Kind (Sohn).
Rufen Sie die changeProps-Methode Child (son) auf
Überprüfen Sie die Ergebnisse.
Die übergeordnete primitive Eigenschaft hat sich nicht geändert
Untergeordnete primitive Eigenschaft geändert (neu geschrieben)
Übergeordnete und untergeordnete object.one-Eigenschaften wurden geändert
Arbeitsbeispiel hier http://jsbin.com/xexurukiso/1/edit/
Weitere Informationen zu Object.create finden Sie hier https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Object/create
quelle