Diferencia entre modelos de vista eliminados declarados como literales de objeto frente a funciones

195

En knockout js veo Ver modelos declarados como:

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

ko.applyBindings(viewModel );

o:

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

ko.applyBindings(new viewModel ());

¿Cuál es la diferencia entre los dos, si hay alguno?

Encontré esta discusión en el grupo de google knockoutjs pero realmente no me dio una respuesta satisfactoria.

Puedo ver una razón si quería inicializar el modelo con algunos datos, por ejemplo:

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

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

Pero si no lo hago, ¿importa qué estilo elijo?

Kev
fuente
No creo que haya una diferencia. Usualmente uso el patrón constructor, ya que a menudo tengo métodos que prefiero declarar en el prototype(métodos que a menudo, por ejemplo, obtienen datos del servidor y actualizan el modelo de vista en consecuencia). Sin embargo, obviamente podría declararlos como una propiedad de un objeto literal, por lo que realmente no puedo ver la diferencia.
James Allardice
44
Esto no tiene nada que ver con la eliminación, y todo que ver con la facilidad de creación de instancias de objetos personalizados en JavaScript
zzzzBov
1
@Kev si viewModel es una función constructora, lo escribe en UpperCase como var PersonViewModel = function () {...};
Elisabeth

Respuestas:

252

Hay un par de ventajas al usar una función para definir su modelo de vista.

La principal ventaja es que tiene acceso inmediato a un valor thisigual a la instancia que se está creando. Esto significa que puedes hacer:

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

Por lo tanto, su observable calculado puede estar vinculado al valor apropiado de this , incluso si se llama desde un ámbito diferente.

Con un objeto literal, deberías hacer:

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

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

En ese caso, podrías usar viewModel directamente en el observable calculado, pero se evalúa de inmediato (por defecto), por lo que no puede definirlo dentro del objeto literal, comoviewModel que no se define hasta después de que se cerró el literal del objeto. A muchas personas no les gusta que la creación de su modelo de vista no esté encapsulada en una sola llamada.

Otro patrón que puede usar para asegurarse de que thissiempre sea apropiado es establecer una variable en la función igual al valor apropiado thisy usarla en su lugar. Esto sería como:

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

Ahora, si está dentro del alcance de un artículo individual y llama $root.removeItem, el valor dethis será en realidad los datos vinculados a ese nivel (que sería el elemento). Al usar self en este caso, puede asegurarse de que se elimine del modelo de vista general.

Otra opción es usar bind, que es compatible con los navegadores modernos y agregado por KO, si no es compatible. En ese caso, se vería así:

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

Hay mucho más que podría decirse sobre este tema y muchos patrones que podría explorar (como el patrón de módulo y el patrón de módulo revelador), pero básicamente el uso de una función le brinda más flexibilidad y control sobre cómo se crea el objeto y la capacidad de referenciar variables que son privadas para la instancia.

RP Niemeyer
fuente
1
Gran respuesta. A menudo uso una función (usando un patrón de módulo revelador) para objetos complejos como modelos de vista. Pero para modelos simples, uso una función para poder manejar todo en un solo lugar.
John Papa
1
@JohnPapa: acabo de ver su video PluralSight en el knockout (un poco más de la mitad) y, casualmente, solo vi la sección sobre objeto literal vs función). Muy bien hecho y ha ayudado a que el centavo caiga. Vale la pena un mes de suscripción solo por eso.
Kev
@Kev - Gracias. Me alegra que estés obteniendo valor de ello. A algunos no les importará ese módulo, ya que no es realmente un concepto Knockout, sino más bien patrones de JavaScript. Pero descubrí a medida que avanzaba con Knockout que esos conceptos realmente me ayudaron a crear un código más limpio y estable. De todos modos, me alegro de que lo disfruten :)
John Papa
no debería ser self.items = ko.observableArray (); en tu segundo ejemplo? Usaste esto, ¿es correcto?
JackNova
1
@JackNova en la función constructor selfy thisson iguales, por lo que cualquiera será equivalente. En la función removeItem, selfse vuelve más útil, ya thisque ya no sería la instancia actual cuando se ejecuta en el contexto de un elemento secundario.
RP Niemeyer
12

Yo uso un método diferente, aunque similar:

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

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

Un par de razones:

  1. No usar this, lo que puede confundir cuando se usa dentro de ko.computeds etc.
  2. Mi viewModel es un singleton, no necesito crear varias instancias (es decir new viewModel())
paulslater19
fuente
Este es un patrón de módulo revelador si no se equivoca. Buena respuesta, pero la pregunta no era sobre este patrón.
Phil
@paul: Perdón por pedir un hilo viejo. usted dijo, My viewModel is a singleton, I don't need to create multiple instances (i.e. new viewModel()) pero no está claro lo que está tratando de decir, ¿ I don't need to create multiple instances puede venir con más uso para que uno pueda entender la ventaja de su enfoque? gracias
Mou
OMI, una de las razones por las que declararías ViewModel como functiones porque lo ejecutarías más de una vez. Sin embargo, en mi ejemplo sobre, es una función anónima invocada de inmediato, por lo que no se creará más de una vez. Es muy similar al Object Literal en el ejemplo anterior, pero te da más aislamiento
paulslater19