Unterschied zwischen als Objektliterale deklarierten Knockout-Ansichtsmodellen und Funktionen

195

In Knockout js sehe ich View Models wie folgt deklariert:

var viewModel = {
    firstname: ko.observable("Bob")
};

ko.applyBindings(viewModel );

oder:

var viewModel = function() {
    this.firstname= ko.observable("Bob");
};

ko.applyBindings(new viewModel ());

Was ist der Unterschied zwischen den beiden, wenn überhaupt?

Ich habe diese Diskussion in der Google-Gruppe von knockoutjs gefunden, aber sie hat mir nicht wirklich eine zufriedenstellende Antwort gegeben.

Ich kann einen Grund erkennen, ob ich das Modell mit einigen Daten initialisieren wollte, zum Beispiel:

var viewModel = function(person) {
    this.firstname= ko.observable(person.firstname);
};

var person = ... ;
ko.applyBindings(new viewModel(person));

Aber wenn ich das nicht mache, spielt es dann eine Rolle, welchen Stil ich wähle?

Kev
quelle
Ich glaube nicht, dass es einen Unterschied gibt. Normalerweise verwende ich das Konstruktormuster, da ich häufig Methoden habe, die ich lieber auf deklariere prototype(Methoden, die beispielsweise häufig Daten vom Server abrufen und das Ansichtsmodell entsprechend aktualisieren). Sie können sie jedoch offensichtlich immer noch als Eigenschaft eines Objektliteral deklarieren, sodass ich keinen wirklichen Unterschied sehe.
James Allardice
4
Dies hat nichts mit Knockout zu tun und alles mit der einfachen Instanziierung von benutzerdefinierten Objekten in JavaScript
zzzzBov
1
@Kev Wenn das viewModel eine Konstruktorfunktion ist, schreiben Sie es in UpperCase genau wie var PersonViewModel = function () {...};
Elisabeth

Antworten:

252

Die Verwendung einer Funktion zum Definieren Ihres Ansichtsmodells bietet einige Vorteile.

Der Hauptvorteil besteht darin, dass Sie sofort auf einen Wert zugreifen können, der thisder erstellten Instanz entspricht. Dies bedeutet, dass Sie Folgendes tun können:

var ViewModel = function(first, last) {
  this.first = ko.observable(first);
  this.last = ko.observable(last);
  this.full = ko.computed(function() {
     return this.first() + " " + this.last();
  }, this);
};

So kann Ihr berechnetes Observable an den entsprechenden Wert von gebunden werden this, selbst wenn es aus einem anderen Bereich aufgerufen wird.

Mit einem Objektliteral müssten Sie Folgendes tun:

var viewModel = {
   first: ko.observable("Bob"),
   last: ko.observable("Smith"),
};

viewModel.full = ko.computed(function() {
   return this.first() + " " + this.last();
}, viewModel);

In diesem Fall können Sie viewModeldas berechnete Observable direkt verwenden, es wird jedoch sofort (standardmäßig) ausgewertet, sodass Sie es nicht innerhalb des Objektliteral definieren können, wie viewModeles erst nach dem Schließen des Objektliterals definiert wird. Vielen Leuten gefällt es nicht, dass die Erstellung Ihres Ansichtsmodells nicht in einem Aufruf zusammengefasst ist.

Ein weiteres Muster, mit dem Sie sicherstellen können, dass thises immer angemessen ist, besteht darin, eine Variable in der Funktion auf den entsprechenden Wert von zu setzen thisund sie stattdessen zu verwenden. Das wäre wie folgt:

var ViewModel = function() {
    var self = this;
    this.items = ko.observableArray();
    this.removeItem = function(item) {
         self.items.remove(item);
    }
};

Wenn Sie sich nun im Bereich eines einzelnen Elements befinden und aufrufen $root.removeItem, ist der Wert von thistatsächlich die Daten, die auf dieser Ebene gebunden werden (was das Element wäre). Wenn Sie in diesem Fall self verwenden, können Sie sicherstellen, dass es aus dem Gesamtansichtsmodell entfernt wird.

Eine andere Option ist die Verwendung bind, die von modernen Browsern unterstützt und von KO hinzugefügt wird, wenn sie nicht unterstützt wird. In diesem Fall würde es so aussehen:

var ViewModel = function() {
    this.items = ko.observableArray();
    this.removeItem = function(item) {
         this.items.remove(item);
    }.bind(this);
};

Zu diesem Thema kann noch viel mehr gesagt werden, und es gibt viele Muster, die Sie untersuchen können (z. B. Modulmuster und Aufdecken von Modulmustern). Die Verwendung einer Funktion bietet Ihnen jedoch mehr Flexibilität und Kontrolle darüber, wie das Objekt erstellt wird und wie Sie darauf verweisen können Variablen, die für die Instanz privat sind.

RP Niemeyer
quelle
1
Gute Antwort. Ich verwende oft eine Funktion (mit aufschlussreichem Modulmuster) für komplexe Objekte wie Ansichtsmodelle. Aber für einfache Modelle verwende ich eine Funktion, damit ich alles an einem Ort erledigen kann.
John Papa
1
@JohnPapa - habe gerade Ihr PluralSight-Video beim Knockout gesehen (etwas mehr als die Hälfte der Zeit - und zufällig nur den Abschnitt über Objektliteral vs. Funktion gesehen). Wirklich gut gemacht und hat geholfen, den Penny fallen zu lassen. Allein dafür lohnt sich ein Monatsabonnement.
Kev
@ Kevin - Danke. Ich bin froh, dass Sie Wert daraus ziehen. Einige werden sich nicht für dieses Modul interessieren, da es nicht wirklich ein Knockout-Konzept ist, sondern eher JavaScript-Muster. Als ich mit Knockout weiter kam, stellte ich fest, dass diese Konzepte mir wirklich geholfen haben, saubereren und stabileren Code zu erstellen. Wie auch immer, ich bin froh, dass es dir gefällt :)
John Papa
sollte nicht self.items = ko.observableArray () sein; in deinem zweiten Beispiel? Du hast das benutzt, ist es richtig?
JackNova
1
@JackNova in der Konstruktorfunktion selfund thissind gleich, so dass beide gleichwertig sind. In der Funktion removeItem wird selfdies nützlicher, da thisdies nicht mehr die aktuelle Instanz ist, wenn sie im Kontext eines untergeordneten Elements ausgeführt wird.
RP Niemeyer
12

Ich benutze eine andere Methode, obwohl ähnlich:

var viewModel = (function () {
  var obj = {};
  obj.myVariable = ko.observable();
  obj.myComputed = ko.computed(function () { return "hello" + obj.myVariable() });

  ko.applyBindings(obj);
  return obj;
})();

Einige Gründe:

  1. Nicht verwenden this, was bei Verwendung innerhalb von ko.computeds usw. Verwirrung stiften kann
  2. Mein viewModel ist ein Singleton, ich muss nicht mehrere Instanzen erstellen (dh new viewModel())
paulslater19
quelle
Dies zeigt das Modulmuster, wenn es nicht falsch ist. Gute Antwort, aber die Frage betraf nicht dieses Muster.
Phil
@paul: Tut mir leid nach altem Thread zu fragen. Du hast gesagt, My viewModel is a singleton, I don't need to create multiple instances (i.e. new viewModel()) aber es ist nicht klar, was du zu sagen I don't need to create multiple instances versuchst. Kannst du mehr Gebrauch machen, damit du den Vorteil deines Ansatzes verstehen kannst? danke
Mou
IMO, einer der Gründe, warum Sie ViewModel als deklarieren würden, functionist, dass Sie es mehr als einmal ausführen würden. In meinem Beispiel handelt es sich jedoch um eine sofort aufgerufene anonyme Funktion, sodass sie nur einmal erstellt wird. Es ist dem Objektliteral im obigen Beispiel sehr ähnlich, bietet Ihnen jedoch mehr Isolation
paulslater19