Différence entre les modèles de vue désactivables déclarés en tant que littéraux d'objets et fonctions

195

Dans knockout js, je vois View Models déclaré comme:

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

ko.applyBindings(viewModel );

ou:

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

ko.applyBindings(new viewModel ());

Quelle est la différence entre les deux, le cas échéant?

J'ai trouvé cette discussion sur le groupe google knockoutjs mais cela ne m'a pas vraiment donné de réponse satisfaisante.

Je peux voir une raison si je voulais initialiser le modèle avec quelques données, par exemple:

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

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

Mais si je ne le fais pas, quel style choisir?

Kev
la source
Je ne pense pas qu'il y ait de différence. J'utilise généralement le modèle de constructeur, car j'ai souvent des méthodes que je préfère déclarer sur prototype(des méthodes qui, souvent, par exemple, récupèrent les données du serveur et mettent à jour le modèle de vue en conséquence). Cependant, vous pouvez toujours les déclarer comme une propriété d'un objet littéral, donc je ne vois pas vraiment de différence.
James Allardice
4
Cela n'a rien à voir avec le knockout, et tout à voir avec la facilité d'instanciation d'objets personnalisés en JavaScript
zzzzBov
1
@Kev si le viewModel est une fonction constructeur, vous l'écrivez en UpperCase comme var PersonViewModel = function () {...};
Elisabeth

Réponses:

252

Il y a quelques avantages à utiliser une fonction pour définir votre modèle de vue.

Le principal avantage est que vous avez un accès immédiat à une valeur thiségale à l'instance en cours de création. Cela signifie que vous pouvez faire:

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

Ainsi, votre observable calculé peut être lié à la valeur appropriée de this, même s'il est appelé à partir d'une portée différente.

Avec un objet littéral, il faudrait faire:

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

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

Dans ce cas, vous pouvez utiliser viewModel directement dans l'observable calculé, mais il est évalué immédiatement (par défaut), vous ne pouvez donc pas le définir dans le littéral de l'objet, commeviewModel n'est défini qu'après la fermeture du littéral objet. Beaucoup de gens n'aiment pas que la création de votre modèle de vue ne soit pas encapsulée dans un seul appel.

Un autre modèle que vous pouvez utiliser pour vous assurer qu'il thisest toujours approprié consiste à définir une variable dans la fonction égale à la valeur appropriée de thiset à l'utiliser à la place. Ce serait comme:

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

Maintenant, si vous êtes dans la portée d'un élément individuel et appelez $root.removeItem , la valeur de thissera en fait les données liées à ce niveau (qui serait l'élément). En utilisant self dans ce cas, vous pouvez vous assurer qu'il est supprimé du modèle de vue globale.

Une autre option utilise bind, qui est prise en charge par les navigateurs modernes et ajoutée par KO, si elle n'est pas prise en charge. Dans ce cas, cela ressemblerait à:

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

Il y a beaucoup plus à dire sur ce sujet et de nombreux modèles que vous pouvez explorer (comme le modèle de module et le modèle de module révélateur), mais en gros, l'utilisation d'une fonction vous donne plus de flexibilité et de contrôle sur la façon dont l'objet est créé et la capacité de référence variables privées de l'instance.

RP Niemeyer
la source
1
Très bonne réponse. J'utilise souvent une fonction (en utilisant un modèle de module révélateur) pour des objets complexes comme les modèles d'affichage. Mais pour les modèles simples, j'utilise une fonction pour pouvoir tout gérer en un seul endroit.
John Papa
1
@JohnPapa - je viens de regarder votre vidéo PluralSight sur knockout (un peu plus à mi-chemin - et, par coïncidence, j'ai juste regardé la section sur la fonction vs littéral objet). Vraiment bien fait et a contribué à la baisse du sou. Il vaut bien un mois d'abonnement pour cela seul.
Kev
@Kev - Merci. Heureux que vous en tiriez de la valeur. Certains ne se soucient pas de ce module car ce n'est pas vraiment un concept Knockout, plus de modèles JavaScript. Mais j'ai découvert en avançant avec Knockout que ces concepts m'ont vraiment aidé à créer un code plus propre et plus stable. Quoi qu'il en soit, heureux que vous l'appréciez :)
John Papa
ne devrait pas être self.items = ko.observableArray (); dans votre deuxième exemple? Vous l'avez utilisé, est-ce correct?
JackNova
1
@JackNova dans la fonction constructeur selfet thissont les mêmes, donc l'un ou l'autre sera équivalent. Dans la fonction removeItem, selfdevient plus utile, comme thisne serait plus l'instance actuelle lorsqu'elle est exécutée dans le contexte d'un élément enfant.
RP Niemeyer
12

J'utilise une méthode différente, mais similaire:

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

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

Quelques raisons:

  1. Ne pas utiliser this, ce qui peut prêter à confusion lorsqu'il est utilisé dans ko.computeds, etc.
  2. Mon viewModel est un singleton, je n'ai pas besoin de créer plusieurs instances (ie new viewModel() )
paulslater19
la source
Cela révèle le modèle du module s'il n'est pas erroné. Bonne réponse mais la question ne concernait pas ce schéma.
Phil
@paul: Désolé de demander l'ancien fil. vous avez dit, My viewModel is a singleton, I don't need to create multiple instances (i.e. new viewModel()) mais ce que vous essayez de dire n'est pas clair, vous I don't need to create multiple instances pouvez utiliser plus d'utilisation pour que vous puissiez comprendre les avantages de votre approche. merci
Mou
OMI, l'une des raisons pour lesquelles vous déclareriez ViewModel en tant que functionest parce que vous l'exécuteriez plus d'une fois. Cependant, dans mon exemple, c'est une fonction anonyme immédiatement invoquée, donc elle ne sera pas créée plus d'une fois. Il est très similaire au Object Literal dans l'exemple ci-dessus, mais vous donne plus d'isolement
paulslater19