Różnica między nokautem Wyświetl modele zadeklarowane jako literały obiektów vs funkcje

195

W nokaut js widzę Wyświetl modele zadeklarowane jako:

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

ko.applyBindings(viewModel );

lub:

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

ko.applyBindings(new viewModel ());

Jaka jest różnica między nimi, jeśli w ogóle?

Znalazłem tę dyskusję w grupie google knockoutjs, ale tak naprawdę nie dała mi satysfakcjonującej odpowiedzi.

Widzę powód, dla którego chciałbym zainicjować model przy użyciu niektórych danych, na przykład:

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

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

Ale jeśli tego nie robię, to nie ma znaczenia, jaki styl wybiorę?

Kev
źródło
Nie wierzę, że jest różnica. Zwykle używam wzorca konstruktora, ponieważ często mam metody, które wolę deklarować na prototype(metody, które często, na przykład, pobierają dane z serwera i odpowiednio aktualizują model widoku). Jednak nadal możesz oczywiście zadeklarować je jako właściwość literału obiektu, więc tak naprawdę nie widzę różnicy.
James Allardice,
4
Nie ma to nic wspólnego z nokautem, a wszystko z łatwością tworzenia instancji niestandardowych obiektów w JavaScript
zzzzBov
1
@ Kev, jeśli viewModel jest funkcją konstruktora, piszesz go w UpperCase, podobnie jak var PersonViewModel = function () {...};
Elisabeth,

Odpowiedzi:

252

Istnieje kilka zalet używania funkcji do definiowania modelu widoku.

Główną zaletą jest to, że masz natychmiastowy dostęp do wartości thisrównej tworzonej instancji. Oznacza to, że możesz:

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);
};

Zatem obliczona obserwowalna może zostać powiązana z odpowiednią wartością this, nawet jeśli zostanie wywołana z innego zakresu.

Z literałem obiektu musiałbyś zrobić:

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

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

W takim przypadku można użyć viewModelbezpośrednio w obliczonym obserwowalnym, ale jest on oceniany natychmiast (domyślnie), więc nie można go zdefiniować w obrębie literału obiektu, ponieważ viewModelnie jest definiowany, dopóki literał obiektu nie zostanie zamknięty. Wiele osób nie lubi, że tworzenie modelu widoku nie jest zawarte w jednym wywołaniu.

Innym wzorcem, którego można użyć w celu zapewnienia, że thiszawsze jest odpowiedni, jest ustawienie zmiennej w funkcji równej odpowiedniej wartości thisi użycie jej zamiast tego. To byłoby jak:

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

Teraz, jeśli jesteś w zakresie pojedynczego elementu i wywołania $root.removeItem, wartość thisbędzie w rzeczywistości danymi powiązanymi na tym poziomie (który byłby przedmiotem). Używając self w tym przypadku, możesz upewnić się, że jest ona usuwana z ogólnego modelu widoku.

Inną opcją jest używanie bind, które jest obsługiwane przez nowoczesne przeglądarki i dodawane przez KO, jeśli nie jest obsługiwane. W takim przypadku wyglądałoby to tak:

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

Jest wiele więcej do powiedzenia na ten temat i wiele wzorców, które można zbadać (np. Wzorzec modułu i ujawnianie wzorca modułu), ale w zasadzie użycie funkcji daje większą elastyczność i kontrolę nad sposobem tworzenia obiektu oraz możliwością odniesienia zmienne, które są prywatne dla instancji.

RP Niemeyer
źródło
1
Świetna odpowiedź. Często używam funkcji (przy użyciu wzoru modułu odsłaniającego) w przypadku złożonych obiektów, takich jak modele widoków. Ale w przypadku prostych modeli używam funkcji, dzięki czemu mogę obsłużyć wszystko w jednym miejscu.
John Papa,
1
@JohnPapa - właśnie oglądałem Twój film PluralSight na temat nokautu (nieco ponad półmetka) i, przypadkiem, po prostu oglądałem sekcję o dosłowności obiektu względem funkcji). Naprawdę dobrze zrobione i pomogło upuścić grosz. Samo warte subskrypcji miesięcznej.
Kev
@Kev - Dzięki. Cieszę się, że czerpiesz z tego wartość. Niektórym nie zależy na tym module, ponieważ tak naprawdę nie jest to koncepcja Knockout, więcej wzorców JavaScript. Jednak wraz z Knockout odkryłem, że te koncepcje naprawdę pomogły mi stworzyć czystszy, bardziej stabilny kod. Tak czy inaczej, cieszę się, że ci się spodoba :)
John Papa,
nie powinno być self.items = ko.observableArray (); w twoim drugim przykładzie? Użyłeś tego, prawda?
JackNova
1
@JackNova w funkcji konstruktora selfi thissą takie same, więc oba będą równoważne. W funkcji removeItem selfstaje się bardziej użyteczna, ponieważ thisnie byłaby już bieżącą instancją, gdy zostanie wykonana w kontekście elementu potomnego.
RP Niemeyer,
12

Używam innej metody, choć podobnej:

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

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

Kilka powodów:

  1. Nieużywanie this, które może wprowadzać w błąd, gdy jest używane w ko.computeds itp
  2. My viewModel jest singletonem, nie muszę tworzyć wielu instancji (tj. new viewModel())
paulslater19
źródło
To ujawnia wzór modułu, jeśli się nie pomyli. Dobra odpowiedź, ale pytanie nie dotyczyło tego wzoru.
Phil
@paul: Przepraszam, że poprosiłem o stary wątek. powiedziałeś, My viewModel is a singleton, I don't need to create multiple instances (i.e. new viewModel()) ale nie jest jasne, co próbujesz powiedzieć, czy I don't need to create multiple instances możesz użyć więcej, aby zrozumieć zaletę twojego podejścia. dzięki
Mou
IMO, jednym z powodów, dla których deklarujesz ViewModel jako, jest functionto, że wykonasz go więcej niż raz. Jednak w moim przykładzie jest to anonimowa funkcja natychmiast wywołana, więc nie zostanie utworzona więcej niż raz. Jest bardzo podobny do Object Literal w powyższym przykładzie, ale daje więcej izolacji
paulslater19