viewmodel .prototype .function vs self .function in a viewmodel?
Asked Answered
P

1

10

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

Pachston answered 4/10, 2014 at 23:18 Comment(0)
P
13

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.

Papaverine answered 5/10, 2014 at 0:41 Comment(3)
Helpful clarification - what do you mean by 'the revealing module pattern' ? BTW, I just stumbled on your Knockout AMD Helpers - looks extremely useful - especially like modules containing their own templates. So thanks too for that!Pachston
There are a few links out there describing the revealing module pattern, here is one: weblogs.asp.net/dwahlin/…. Basically, you define whatever methods that you want internally and are able to reference variables directly. Then, you return the public "API" from the module with the methods that you want to expose with whatever names that you want to expose them with. Hope that helps!Papaverine
@RPNiemeyer - didn't you write a blog on this exact question? I thought I recalled reading that in the past and have been googling and this is where I ended upShabuoth

© 2022 - 2024 — McMap. All rights reserved.