1

My JS is organized into viewmodels and services. The services are mostly concerned with AJAX calls, whereas my viewModels describe the view that they're used in.

I have two view models now - StoreViewModel, and MyStoreViewModel. In each of these, I have the following:

function MyStoreVm(model) {
    var self = this;

    self.doThis = function(){
        // do stuff 
        self.doThat();
    };
}

Then:

function StoreVm(model) {
    var self = this;

    self.doThis = function(){
        // do stuff 
        self.doThat();
    };
}

I come from a C# background - normally I would just use inheritance in this kind of situation. How can I eliminate this code repetition between two distinct modules / viewmodels, by having them inherit from a third, shared module?

More details: These are being used in an MVC view where I have knockout bindings depending on whether or not the store is MyStore:

@if (!Model.IsMyStore) {
    <script type="text/javascript">
        $(document).ready(ko.applyBindings(new StoreVm(@Html.Raw(JsonConvert.SerializeObject(Model, new JsonSerializerSettings() { ContractResolver = new CamelCasePropertyNamesContractResolver() })))));
    </script>
} else if (Model.IsMyStore) {
    <script type="text/javascript">
        $(document).ready(ko.applyBindings(new MyStoreVm(@Html.Raw(JsonConvert.SerializeObject(Model, new JsonSerializerSettings() { ContractResolver = new CamelCasePropertyNamesContractResolver() }).Sanitize()))));
    </script>
}

UPDATE

I looked into some of the suggestions below, but none seemed clean and simple enough for my novice skills. I tried the following which seems to work:

function BaseStore(model){
    self.doThis = function(){
        // do stuff 
        self.doThat();
    };
    // and a whole lot of other shared code
}

function StoreVm(model) {
    var storeVm = new BaseStoreVm(model)
    var self = storeVm;
    self.isolatedFunctionForGenericStores = function(){stuff}
    // other stuff for only this type
    return storeVm;
}

function MyStoreVm(model) {
    var myStoreVm = new BaseStoreVm(model)
    var self = myStoreVm;
    self.isolatedFunctionForMyStore = function(){stuff}
    // other stuff for only this type
    return myStoreVm;
}

Is there anything wrong with this approach?

SB2055
  • 12,272
  • 32
  • 97
  • 202

3 Answers3

2

If you have two child types that need to inherit from the same parent, you can do:

function Parent( foo ) {
  this.foo = foo;
}
Parent.prototype.method = function() {
  console.log( this.foo );
};

function Child1() {
  Parent.call( this, "bar" );
}
Child1.prototype = Object.create( Parent.prototype );
Child1.prototype.constructor = Child1;

function Child2() {
  Parent.call( this, "qux" );
}
Child2.prototype = Object.create( Parent.prototype );
Child2.prototype.constructor = Child2;

var parent = new Parent("blah");
var child1 = new Child1();
var child2 = new Child2();

parent.method();  // => "blah"
child1.method();  // => "bar"
child2.method();  // => "qux"
sbking
  • 7,630
  • 24
  • 33
  • Sorry - they should both inherit from a single other function. – SB2055 Dec 03 '13 at 03:13
  • Thank you for this. I tried your suggestion but got myself confused while putting everything together. I was able to get it working via the code in my update - what are your thoughts on this approach? – SB2055 Dec 05 '13 at 00:30
0

First you should understand how JavaScript implement inheritance. JavaScript is a prototype-based language which contains no class statement, such as is found in C#. Instead, it uses functions as classes(no classes, just objects).
So what we have here is objects inherit from other objects (now you might need to get some coffee).

So then JavaScript does not give you the full power of inheritance and polymorphism you get in C#.
If you want to know ways to implement inheritance in JS:

  1. SO: Performing inheritance in JavaScript
  2. My Blog: Javascript Inheritance techniques

Back to your question, i think you may need to implement The Factory Pattern. So your js code could be like that:

function MyStoreVm(model) {
    var self = this;
    self.doThis = function() {
        // do stuff 
        self.doThat();
    };
}

function StoreVm(model) {
    var self = this;
    self.doThis = function() {
        // do stuff 
        self.doThat();
    };
}
// Define factory object that create your proper store object
// StoreFactory takes the model as input.
// You can change it to accept seconf parameter that define class type
function StoreFactory() {
    this.classType == "MyStoreVm"; // default value
    this.createStore = function(model) {
        if (model.IsMyStore === true)
            this.classType = MyStoreVm;
        else
            this.classType = StoreVm;
        return new this.classType(model);
    }
}

Then in your MVC view:

$(document).ready(function() {
    var mystoreFactory = new StoreFactory();
    ko.applyBindings(mystoreFactory.createStore((@Html.Raw(JsonConvert.SerializeObject(Model, new JsonSerializerSettings() {
        ContractResolver = new CamelCasePropertyNamesContractResolver()
    })))));
});
Community
  • 1
  • 1
ebram khalil
  • 8,252
  • 7
  • 42
  • 60
  • Kind of funny how you mention JavaScript being a prototype based language and then produce sample code that makes no use of it at all. – HMR Dec 03 '13 at 11:43
  • @HMR you right.. i just tried to figure how to solve the problem, not to explain inheritance in details – ebram khalil Dec 03 '13 at 11:56
  • Thanks ebram. Though I'm confused - how does this eliminate the code duplication? I'm trying to get simpler and DRYer by pushing the shared code out of both modules and into one, shared module. – SB2055 Dec 03 '13 at 14:29
  • i think JS do not provide that flexability you want. At the end you must state which sub-class type you want your object to be **not** the the base-class(no polymorphism here in JS) – ebram khalil Dec 03 '13 at 15:59
0

Check out Klass.js. While this is basically the same as creating your own prototypes and inheritance methods, it's nice to use. It's also AMD aware.

// base class
var Store = klass(function() {
    var self = this;
    // add properties here
}).methods({
    doThis: function () {
        // do this
    },
    doThat: function () {
        // do that
    }
});
return Store;

// create the first constructor
var myStoreVm = Store.extend(function () {
    // super class is called
}).methods({
    doThis: function(){
        this.supr(); // call Store.doThis
        // some other code
    }
});
return myStoreVm;

// create the second constructor
var storeVm = Store.extend(function () {
    // super class is called
}).methods({
    doThis: function(){
        // override Store.doThis with my own code
    }
});
return storeVm;
Space Monkey
  • 981
  • 1
  • 9
  • 14