Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

viewmodel .prototype .function vs self .function in a viewmodel?

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.)

like image 229
Howard Pautz Avatar asked Oct 04 '14 23:10

Howard Pautz


1 Answers

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.

like image 168
RP Niemeyer Avatar answered Oct 20 '22 17:10

RP Niemeyer