1056 Stimmen

'this' vs $scope in AngularJS-Controllern

Im Abschnitt "Komponenten erstellen" auf der Homepage von AngularJS gibt es dieses Beispiel:

controller: function($scope, $element) {
  var panes = $scope.panes = [];
  $scope.select = function(pane) {
    angular.forEach(panes, function(pane) {
      pane.selected = false;
    });
    pane.selected = true;
  }
  this.addPane = function(pane) {
    if (panes.length == 0) $scope.select(pane);
    panes.push(pane);
  }
}

Beachten Sie, wie die Methode select zu $scope hinzugefügt wird, aber die Methode addPane zu this. Wenn ich es zu $scope.addPane ändere, bricht der Code ab.

Die Dokumentation besagt, dass es tatsächlich einen Unterschied gibt, aber sie erwähnt nicht, was der Unterschied ist:

Frühere Versionen von Angular (vor 1.0 RC) erlaubten es Ihnen, this austauschbar mit der $scope-Methode zu verwenden, aber das ist nicht mehr der Fall. Innerhalb von Methoden, die auf dem Scope definiert sind, sind this und $scope austauschbar (Angular setzt this auf $scope), aber nicht anderswo innerhalb Ihres Controller-Konstruktors.

Wie funktionieren this und $scope in AngularJS-Controllern?

1 Stimmen

Ich finde das auch verwirrend. Wenn eine Ansicht einen Controller spezifiziert (z.B., ng-controller='...'), scheint der damit verbundene $scope ebenfalls mitzukommen, da die Ansicht auf $scope-Eigenschaften zugreifen kann. Aber wenn eine Direktive einen anderen Controller 'require't (und ihn dann in ihrer Linking-Funktion verwendet), kommt der damit verbundene $scope nicht mit?

0 Stimmen

Ist dieses verwirrende Zitat über "Vorherige Versionen..." mittlerweile entfernt worden? Dann wäre vielleicht ein Update angebracht?

0 Stimmen

Für Unittesting, wenn Sie 'this' anstelle von '$scope' verwenden, können Sie den Controller nicht mit einem gemockten Scope injizieren und somit keine Unittests durchführen. Ich glaube nicht, dass es eine gute Praxis ist, 'this' zu verwenden.

1014voto

Mark Rajcok Punkte 356006

"Wie funktionieren this und $scope in AngularJS-Controllern?"

Kurze Antwort:

  • this
    • Wenn die Controller-Konstruktorfunktion aufgerufen wird, ist this der Controller.
    • Wenn eine Funktion, die auf einem $scope-Objekt definiert ist, aufgerufen wird, ist this das "Scope, das zum Zeitpunkt des Funktionsaufrufs wirksam war". Dies kann (oder auch nicht!) das $scope sein, auf dem die Funktion definiert ist. Daher können this und $scope innerhalb der Funktion nicht dasselbe sein.
  • $scope
    • Jeder Controller hat ein zugehöriges $scope-Objekt.
    • Eine Controller-(Konstruktor-)Funktion ist dafür verantwortlich, Modell-Eigenschaften und Funktionen/Verhalten auf seinem zugehörigen $scope zu setzen.
    • Nur Methoden, die auf diesem $scope-Objekt definiert sind (und auf Eltern-Scope-Objekten, wenn prototypische Vererbung im Spiel ist), sind vom HTML/View aus zugänglich. Z.B. von ng-click, Filtern, etc.

Lange Antwort:

Eine Controller-Funktion ist eine JavaScript-Konstruktorfunktion. Wenn die Konstruktorfunktion ausgeführt wird (z.B. wenn eine Ansicht geladen wird), wird this (d.h., der "Funktionskontext") auf das Controller-Objekt gesetzt. Also in der "Tabs"-Controller-Konstruktorfunktion, wenn die addPane-Funktion erstellt wird

this.addPane = function(pane) { ... }

wird sie auf dem Controller-Objekt erstellt, nicht auf dem $scope. Die Ansichten können die addPane-Funktion nicht sehen - sie haben nur Zugriff auf Funktionen, die auf $scope definiert sind. Mit anderen Worten, im HTML funktioniert dies nicht:

funktioniert nicht

Nachdem die "Tabs"-Controller-Konstruktorfunktion ausgeführt wurde, haben wir folgendes:

nach Tabs-Controller-Konstruktorfunktion

Die gestrichelte schwarze Linie zeigt die prototypische Vererbung an - ein isoliertes Scope erbt prototypisch von Scope. (Es erbt nicht prototypisch von dem Scope, der zum Zeitpunkt des Auftretens der Direktive im HTML wirksam war.)

Jetzt möchte die Link-Funktion der Panel-Direktive mit der Tabs-Direktive kommunizieren (was eigentlich bedeutet, dass sie das isolierte $scope der Tabs beeinflussen muss). Ereignisse könnten verwendet werden, aber ein anderer Mechanismus besteht darin, dass die Panel-Direktive den Tabs-Controller require. (Es scheint keinen Mechanismus dafür zu geben, dass die Panel-Direktive den Tabs-$scope require.)

Daher stellt sich die Frage: Wenn wir nur Zugriff auf den Tabs-Controller haben, wie erhalten wir Zugriff auf das isolierte Tabs-$scope (was wir wirklich wollen)?

Nun, die rote gestrichelte Linie ist die Antwort. Der "Scope" der addPane()-Funktion (hier meine ich JavaScripts Funktionsumgebung/Schlussfolgerungen) gibt der Funktion Zugriff auf das isolierte Tabs-$scope. D.h., addPane() hat Zugriff auf das "Tabs IsolateScope" im obigen Diagramm aufgrund eines Schlussfolgerung, das erstellt wurde, als addPane() definiert wurde. (Wenn wir stattdessen addPane() auf dem Tabs-$scope-Objekt definiert hätten, hätte die Panel-Direktive keinen Zugriff auf diese Funktion, und daher hätte sie keine Möglichkeit, mit dem Tabs-$scope zu kommunizieren.)

Um die andere Hälfte Ihrer Frage zu beantworten: wie funktioniert $scope in Controllern?:

Innerhalb von auf $scope definierten Funktionen wird this auf "das $scope, das zum Zeitpunkt des Funktionsaufrufs wirksam war" gesetzt. Angenommen, wir haben das folgende HTML:

   "this" und $scope protokollieren - übergeordnetes Scope

      "this" und $scope protokollieren - untergeordnetes Scope

Und das ParentCtrl hat (ausschließlich)

$scope.logThisAndScope = function() {
    console.log(this, $scope)
}

Wenn Sie auf den ersten Link klicken, wird angezeigt, dass this und $scope gleich sind, da "das zum Funktionsaufruf wirksame Scope" das Scope ist, das mit dem ParentCtrl verbunden ist.

Wenn Sie auf den zweiten Link klicken, wird deutlich, dass this und $scope nicht gleich sind, da "das zum Funktionsaufruf wirksame Scope" das Scope ist, das mit dem ChildCtrl verbunden ist. Hier ist also this auf das $scope von ChildCtrl gesetzt. Innerhalb der Methode ist $scope immer noch das $scope von ParentCtrl.

Fiddle

Ich versuche, innerhalb einer auf $scope definierten Funktion nicht this zu verwenden, da es verwirrend wird, welches $scope betroffen ist, insbesondere wenn ng-repeat, ng-include, ng-switch und Direktiven alle ihre eigenen untergeordneten Scopes erstellen können.

1 Stimmen

Wenn eine Funktion, die auf einem $scope-Objekt definiert ist, aufgerufen wird, handelt es sich um den "Scope, der zum Zeitpunkt des Funktionsaufrufs in Kraft tritt". Dies kann (oder auch nicht!) der $scope sein, auf dem die Funktion definiert ist. Also können in der Funktion this und $scope möglicherweise nicht dasselbe sein. Aber die offizielle AngularJS-Dokumentation besagt, dass innerhalb von auf dem Scope definierten Methoden this und $scope austauschbar sind (Angular setzt this auf $scope), jedoch nicht innerhalb Ihres Controller-Konstruktors.

6 Stimmen

@tamakisquare, Ich glaube, dass der fettgedruckte Text, den du zitiert hast, sich darauf bezieht, wenn die Controller-Konstruktorenfunktion aufgerufen wird - d.h. wenn der Controller erstellt wird und mit einem $scope verbunden ist. Es gilt nicht später, wenn beliebiger JavaScript-Code eine Methode aufruft, die auf einem $scope-Objekt definiert ist.

0 Stimmen

Vielen Dank, Mark. Ich habe den Teil des offiziellen Dokuments noch ein paar Mal gelesen; Der sechste Punkt nach "Ein würziges Controller-Beispiel". Ich denke immer noch, dass das Dokument besagt, dass this und $scope austauschbar sind, wenn sie in Methoden verwendet werden, die im Scope definiert sind. Es wird nicht erwähnt, dass die Austauschbarkeit davon abhängt, wo die Methoden aufgerufen werden, also gehe ich davon aus, dass das keine Rolle spielt. Aber offensichtlich hat dein Beispiel das Gegenteil bewiesen.

57voto

Andrew Joslin Punkte 42973

Der Grund, warum 'addPane' dies zugewiesen wird, liegt an der Direktive.

Die pane Direktive hat require: '^tabs', was den Tabs Controller-Objekt aus einer Eltern-Direktive in die Link-Funktion einfügt.

addPane wird this zugewiesen, damit die pane Link-Funktion darauf zugreifen kann. Dann ist in der pane Link-Funktion addPane einfach eine Eigenschaft des tabs Controllers, und es ist einfach tabsControllerObject.addPane. So kann die Link-Funktion der pane Direktive auf das Tabs Controller-Objekt zugreifen und daher auf die addPane-Methode zugreifen.

Ich hoffe, meine Erklärung ist klar genug.. es ist irgendwie schwer zu erklären.

3 Stimmen

Vielen Dank für die Erklärung. Die Dokumentation lässt es so aussehen, als wäre der Controller einfach eine Funktion, die den Scope einrichtet. Warum wird der Controller behandelt wie ein Objekt, wenn sich alle Aktionen im Scope abspielen? Warum nicht einfach den übergeordneten Scope in die Verknüpfungsfunktion übergeben? Bearbeitung: Um diese Frage besser zu formulieren, wenn Controller-Methoden und Scope-Methoden beide auf der gleichen Datenstruktur (dem Scope) arbeiten, warum sie dann nicht alle an einem Ort platzieren?

0 Stimmen

Es scheint, dass der Elternbereich nicht in die lnk-Funktion übergeben wird, weil der Wunsch besteht, "wiederverwendbare Komponenten zu unterstützen, die versehentlich Daten im Elternbereich nicht lesen oder ändern sollten". Aber wenn eine Direktive wirklich einige bestimmte Daten im Elternbereich lesen oder ändern möchte / muss (wie die 'pane' Direktive), erfordert dies einige Anstrengungen: 'require' den Controller, in dem der gewünschte Elternbereich liegt, dann definiere eine Methode auf diesem Controller (verwende 'this' nicht $scope), um auf spezifische Daten zuzugreifen. Da der gewünschte Elternbereich nicht in die lnk-Funktion injiziert wird, vermute ich, dass dies der einzige Weg ist, es zu tun.

1 Stimmen

Hallo Mark, es ist eigentlich einfacher, den Geltungsbereich der Direktive zu ändern. Du kannst einfach die Link-Funktion verwenden jsfiddle.net/TuNyj

28voto

Derek Punkte 722

Ich habe gerade eine ziemlich interessante Erklärung über den Unterschied zwischen den beiden gelesen und eine wachsende Präferenz, Modelle an den Controller anzuhängen und den Controller an das Modell zu binden. http://toddmotto.com/digging-into-angulars-controller-as-syntax/ ist der Artikel.

HINWEIS: Der ursprüngliche Link existiert immer noch, aber Änderungen im Format haben es schwer lesbar gemacht. Es ist einfacher, es im Original anzusehen.

Er erwähnt es nicht, aber beim Definieren von Direktiven, wenn Sie etwas zwischen mehreren Direktiven teilen müssen und keinen Service möchten (es gibt legitime Fälle, in denen Services umständlich sind), hängen Sie die Daten an den Controller der übergeordneten Direktive an.

Der $scope Service bietet viele nützliche Funktionen, $watch ist dabei das Offensichtlichste, aber wenn Sie nur Daten an die Ansicht binden müssen, ist die Verwendung des einfachen Controllers und von 'controller as' im Template in Ordnung und möglicherweise bevorzugt.

19voto

Liran Brimer Punkte 3188

Ich empfehle Ihnen, den folgenden Beitrag zu lesen: AngularJS: "Controller as" oder "$scope"?

Es beschreibt sehr gut die Vorteile der Verwendung von "Controller as" zur Exposition von Variablen über "$scope".

Ich weiß, dass Sie speziell nach Methoden und nicht nach Variablen gefragt haben, aber ich denke, es ist besser, eine Technik beizubehalten und konsequent damit zu sein.

Also meiner Meinung nach, aufgrund der in dem Beitrag diskutierten Variablenproblematik, ist es besser, einfach die "Controller as"-Technik zu verwenden und sie auch auf die Methoden anzuwenden.

0 Stimmen

Der Link scheint tot zu sein

16voto

Sandro Punkte 211

In diesem Kurs (https://www.codeschool.com/courses/shaping-up-with-angular-js) erklären sie, wie man "this" und viele andere Dinge verwendet.

Wenn Sie eine Methode zum Controller mit der Methode "this" hinzufügen, müssen Sie sie in der Ansicht mit dem Namen des Controllers "Punkt" Ihrer Eigenschaft oder Methode aufrufen.

Zum Beispiel, wenn Sie Ihren Controller in der Ansicht verwenden, könnte Ihr Code so aussehen:

       Ihr erster Abschnitt ist {{aliasOfYourController.panes[0]}}

6 Stimmen

Nachdem ich den Kurs durchgegangen bin, war ich sofort verwirrt von dem Code, der $scope verwendet, also danke, dass du es erwähnt hast.

16 Stimmen

Dieser Kurs erwähnt überhaupt nicht $scope, sie verwenden nur as und this, also wie kann es helfen, den Unterschied zu erklären?

10 Stimmen

Mein erster Kontakt mit Angular war im genannten Kurs, und da $scope nie erwähnt wurde, habe ich gelernt, in Controllern nur this zu verwenden. Das Problem ist, dass wenn man anfängt, Promises im Controller zu behandeln, man viele Referenzprobleme zu this hat und anfangen muss, Dinge wie var me = this zu tun, um das Modell in this aus der Promise-Rückgabefunktion heraus zu referenzieren. Deshalb bin ich immer noch sehr verwirrt darüber, welche Methode verwendet werden sollte, $scope oder this.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X