Diferença entre os Modelos de Visualização knockout declarados como literais de object versus funções

No js knockout eu vejo modelos de view declarados como:

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

ou:

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

Qual a diferença entre os dois, se houver?

Eu encontrei essa discussão sobre o grupo do google knockoutjs, mas realmente não me deu uma resposta satisfatória.

Eu posso ver um motivo se eu quisesse inicializar o modelo com alguns dados, por exemplo:

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

Mas se eu não estou fazendo isso, importa qual estilo eu escolho?

Há algumas vantagens em usar uma function para definir seu modelo de visualização.

A principal vantagem é que você tem access imediato a um valor this que é igual à instância que está sendo criada. Isso significa que você pode fazer:

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

Assim, seu observável calculado pode ser vinculado ao valor apropriado this , mesmo se chamado de um escopo diferente.

Com um literal de object, você teria que fazer:

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

Nesse caso, você poderia usar viewModel diretamente no viewModel observable, mas ele é avaliado de imediato (por padrão), então você não poderia defini-lo dentro do literal do object, já que viewModel não é definido até que o literal do object seja fechado. Muitas pessoas não gostam que a criação do seu modelo de visualização não seja encapsulada em uma única chamada.

Outro padrão que você pode usar para garantir que this seja sempre apropriado é definir uma variável na function igual ao valor apropriado e usá-la. Isso seria como:

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

Agora, se você estiver no escopo de um item individual e chamar $root.removeItem , o valor this será realmente os dados sendo vinculados a esse nível (que seria o item). Ao usar self nesse caso, você pode garantir que ele esteja sendo removido do modelo de visualização geral.

Outra opção é usar bind , que é suportado por navegadores modernos e adicionado por KO, se não for suportado. Nesse caso, seria parecido com:

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

Há muito mais que poderia ser dito sobre esse tópico e muitos padrões que você poderia explorar (como padrão de módulo e padrão de módulo de revelação), mas basicamente usar uma function fornece mais flexibilidade e controle sobre como o object é criado e a capacidade de fazer referência variables ​​que são privadas para a instância.

Eu uso um método diferente, embora semelhante:

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

Algumas razões:

  1. Não usando this , o que pode confusão quando usado dentro ko.computed s etc
  2. Meu viewModel é um singleton, eu não preciso criar várias instâncias (ou seja, new viewModel() )