Both code blocks below work, in context and seem completely functionally equivalent. I understand js prototypes reasonably well, so I'm not asking about them per se (unless that is the only difference).
Rather, comparing two simple ways to put a method on a view model as shown below, are there any implications / differences for Knockout, e.g. binding time?
define(["knockout", "text!./home.html"], function(ko, homeTemplate) { // <-- An AMD Module
function HomeViewModel(route) { var self = this; self.message = ko.observable('Snacks!'); self.eatSomething = function () { self.message('Yum, a viewmodel snack.'); }; } return { viewModel: HomeViewModel, template: homeTemplate }; });
versus adding method via prototype:
define(["knockout", "text!./home.html"], function(ko, homeTemplate) {
function HomeViewModel(route) { this.message = ko.observable('Snacks!'); }; HomeViewModel.prototype.eatSomething = function () { this.message('Yum, the same viewmodel snack, only different?'); }; return { viewModel: HomeViewModel, template: homeTemplate };
});
(The code is a simple mod of Yeoman's scaffolding output via a Knockout generator. It created the boiler plate code for a knockout component, a fairly recent (KO 3.2) and very welcome feature. A nice KO component explainer is here.)
In the first example, since the function uses self
(which is set to a reference to the new instance) rather than this
, no matter how the function is called/bound, it would always correctly set its own message
observable.
In the second example, when binding normally to the function like data-bind="click: eatSomething"
, you would get the same result. Knockout calls the function with the value of this
equal to the current data.
If you had a scenario where you needed to call the function from a different context (maybe your view model has a child object that you are using with
or a template against). then you might use a binding like data-bind="click: $parent.eatSomething"
. KO would still call the function with this
equal to the current data (which would not be $parent
), so you would have an issue.
One advantage of putting the function on the prototype though, is that if you created many instances of HomeViewModel
they would all share the same eatSomething
function through their prototype, rather than each instance getting its own copy of the eatSomething
function. That may not be a concern in your scenario, as you may only have one HomeViewModel
.
You can ensure that the context is correct by calling it like: data-bind="click: $parent.eatSomething.bind($parent)
. Using this call, would create a new wrapper function that calls the original with the appropriate context (value of this
). Alternatively, you could bind it in the view model as well to keep the binding cleaner. Either way, you do lose some of the value of putting it on the prototype, as you are creating wrapper functions for each instance anyways. The "guts" of the function would only exist once on the prototype though at least.
I tend to use prototypes in my code, but there is no doubt that using the self
technique (or something like the revealing module pattern) can reduce your concern with the value of this
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With