Разница между нокаут-моделями представления, объявленными как объектные литералы, по сравнению с функциями

195

В нокауте js я вижу View Models, объявленные как:

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

ko.applyBindings(viewModel );

или:

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

ko.applyBindings(new viewModel ());

В чем разница между двумя, если таковые имеются?

Я нашел это обсуждение в группе Google knockoutjs, но оно не дало мне удовлетворительного ответа.

Я вижу причину, если я хотел инициализировать модель с некоторыми данными, например:

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

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

Но если я не занимаюсь этим, имеет ли значение, какой стиль я выберу?

Кев
источник
Я не верю, что есть разница. Я обычно использую шаблон конструктора, так как у меня часто есть методы, которые я предпочитаю объявлять prototype(методы, которые часто, например, выбирают данные с сервера и соответственно обновляют модель представления). Однако вы все равно можете объявить их как свойство литерала объекта, поэтому я не вижу разницы.
Джеймс Аллардис
4
Это не имеет ничего общего с нокаутом, а связано с простотой создания пользовательских объектов в JavaScript
zzzzBov
1
@Kev, если viewModel является функцией конструктора, вы пишете ее в UpperCase, как var PersonViewModel = function () {...};
Элизабет

Ответы:

252

Есть несколько преимуществ использования функции для определения модели представления.

Основным преимуществом является то, что у вас есть немедленный доступ к значению this, равному создаваемому экземпляру. Это означает, что вы можете сделать:

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

Таким образом, ваша вычисляемая наблюдаемая может быть связана с соответствующим значением this, даже если она вызывается из другой области видимости.

С литералом объекта вы должны сделать:

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

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

В этом случае вы можете использовать viewModelнепосредственно в вычисляемой наблюдаемой, но она оценивается немедленно (по умолчанию), поэтому вы не можете определить ее в литерале объекта, как viewModelэто не определено до тех пор, пока литерал объекта не будет закрыт. Многим людям не нравится, что создание вашей модели представления не заключено в один вызов.

Другой шаблон, который вы можете использовать, чтобы убедиться, что thisон всегда уместен, - установить переменную в функции равной соответствующему значению thisи использовать ее вместо этого. Это было бы как:

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

Теперь, если вы находитесь в области действия отдельного элемента и вызова $root.removeItem, значение thisфактически будет привязывать данные на этом уровне (который будет элементом). Используя в этом случае self, вы можете убедиться, что он удаляется из общей модели представления.

Другой вариант использования bind, который поддерживается современными браузерами и добавляется KO, если он не поддерживается. В этом случае это будет выглядеть так:

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

По этой теме можно сказать гораздо больше и множество шаблонов, которые вы можете исследовать (например, шаблон модуля и раскрытие шаблона модуля), но в основном использование функции дает вам большую гибкость и контроль над тем, как создается объект, и возможность ссылаться на него. переменные, которые являются частными для экземпляра.

Р.П. Нимейер
источник
1
Отличный ответ. Я часто использую функцию (используя раскрывающийся шаблон модуля) для сложных объектов, таких как модели представления. Но для простых моделей я использую функцию, чтобы я мог обрабатывать все в одном месте.
Джон Папа
1
@JohnPapa - только что смотрел ваше видео PluralSight на нокаут (чуть более половины - и, по совпадению, только что посмотрел раздел об объектном литерале против функции). Действительно хорошо сделано и помогло падение копейки. Хорошо стоит подписка на месяц для этого одного.
Кев
@Kev - Спасибо. Рад, что вы получаете ценность из этого. Некоторым не нужен этот модуль, так как он не является концепцией Knockout, а скорее шаблонами JavaScript. Но с Knockout я обнаружил, что эти концепции действительно помогли мне создать более чистый и стабильный код. В любом случае, рад, что вам понравилось :)
Джон Папа
не должно быть self.items = ko.observableArray (); в вашем втором примере? Вы использовали это, это правильно?
JackNova
1
@JackNova в функции конструктора selfи thisодинаковы, поэтому либо будут эквивалентны. В функции removeItem selfона становится более полезной, поскольку thisбольше не будет текущим экземпляром при выполнении в контексте дочернего элемента.
RP Niemeyer
12

Я использую другой метод, хотя и похожий:

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

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

Пара причин:

  1. Не используется this, что может привести к путанице при использовании в ko.computeds и т. Д.
  2. Моя viewModel является одиночной, мне не нужно создавать несколько экземпляров (то есть new viewModel())
paulslater19
источник
Это показывает шаблон модуля, если не ошибаюсь. Хороший ответ, но вопрос был не об этой схеме.
Фил
@Paul: Извините, чтобы попросить старую ветку. Вы сказали, My viewModel is a singleton, I don't need to create multiple instances (i.e. new viewModel()) но не ясно, что вы пытаетесь сказать, I don't need to create multiple instances можете ли вы использовать PLZZ больше, чтобы можно было понять преимущества вашего подхода. спасибо
Моу
IMO, одна из причин, по которой вы объявите ViewModel как, functionзаключается в том, что вы выполняете его более одного раза. Тем не менее, в моем примере о, это немедленно вызванная анонимная функция, поэтому она не будет создана более одного раза. Он очень похож на Object Literal в приведенном выше примере, но дает вам больше изоляции
paulslater19