This question is an improvement I did based on recommendations from these other questions: Simple object-oriented calculatorSimple object-oriented calculator - follow-upTo those looking this question first I'm trying to find a correct OOP model I can use in JS. I initially found a very different solution from what's normally done and I received lots of help from some of the good guys here.And I guess I'm finally coming to grips with it. Please take a look.JSFiddle function ViewBase() { Object.defineProperty(this, parse, {   value: function() {   for(prop in this)     this[_.camelCase(prop)] = $('#' + this[prop]);  },  writable: true,  enumerable: false         });};// View class mirroring html componentsfunction ViewCalc () { this.IPT_X = 'x'; this.IPT_Y = 'y'; this.IPT_RES = 'res'; this.BTN_SUM =  'sum'; this.BTN_SUBTRACT =  'subt'; this.BTN_MULTIPLY =  'mult'; this.BTN_DIVISION =  'div'; this.BTN_CLEAN =  'clean'; this.BTN_RAND =  'rand'; this.parse();};ViewCalc.prototype = new ViewBase();function Operands() {    // connect view to the base business class    this.view = new ViewCalc();    //public    this.x = 0;    this.y = 0;    //public    this.showOperands = function() {        //use of a private property (IPT_yyX) and a public property (this.x)        this.view.iptX.val(this.x);        this.view.iptY.val(this.y);    };    this.clean = function() {        this.x = 0;        this.y = 0;        // call to a local public method         this.showOperands();    };    this.updateOperands = function(x, y) {        // use of a public property        this.x = x;        this.y = y;    };    this.clean();};function Randomizer() {    // private    function getRandomNumber() {        return Math.round(Math.random() * 1000);    };    this.updateOperands = function(x, y) {        // call to superior class's method        Randomizer.prototype.updateOperands.call(this, x, y);        // call to method of superior object        this.showOperands();    };    this.populateRandomNumbers = function() {        // call to public local method (this.updateOperands())        // and to a local private method (getRandomNumber()))        this.updateOperands(getRandomNumber(), getRandomNumber());    };    // init    this.populateRandomNumbers();};Randomizer.prototype = new Operands();function Operations() {    //public    this.sum = function() {        // call to 2 local private methods        showRes(doSum());    };    this.subtract = function() {        showRes(doSubtraction());    };    this.multiply = function() {        showRes(doMultiplication());    };    this.division = function() {        showRes(doDivision());    };     var self = this;    // private    function doSum() {        return self.x + self.y;    };    function doSubtraction() {        return self.x - self.y;    };    function doMultiplication() {        return self.x * self.y;    };     function doDivision() {        return self.x / self.y;    };    function showRes(val) {        self.view.iptRes.val(val);    };    // init    this.view.btnSum.on('click', function() { self.sum() });    this.view.btnSubtract.on('click', function() { self.subtract() });    this.view.btnMultiply.on('click', function() { self.multiply() });    this.view.btnDivision.on('click', function() { self.division() });       this.view.btnClean.on('click', function() { self.clean() });    this.view.btnRand.on('click', function() { self.populateRandomNumbers()     });};Operations.prototype = new Randomizer();var o = new Operations();<html><body>X: <input id='x'><br>Y: <input id='y'><br>Res: <input id='res'><br><input id='sum' type='button' value='+'><input id='subt' type='button' value='-'><input id='mult' type='button' value='*'><input id='div' type='button' value='/'><input id='clean' type='button' value='C'><input id='rand' type='button' value='Rand'><script src=https://cdnjs.cloudflare.com/ajax/libs/lodash.js/3.10.1/lodash.min.js></script><script src=https://ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min.js></script>What I did this time:From @tkellehe's and @SirPython's answers:I'm realizing that using this self variable is causing more harm than does good. So I reverted to this, new and prototype usage.From @tkellehe's answer:I remove all _vars from parameters, all private vars will use it from now on. Just there aren't any at the example at the moment.I used Object.define in the View classes so I don't need to use Object.keys anymore.Because I reverted to this, new and prototype usage, override function doesn't work/it's not needed anymore. So I had to use Class.prototype.method.call instead and I don't need a base class anymore or a salvage to not using new. At least at this point.I know loading within context is important and I'll use it. I just don't think it's needed for this example. But that library loading function was really cool.What still bothers me:I don't like using Class.prototype.method.call. It's too long and too clumsy. Is there a better solution for calling the superior method? Can you make override function work again in this format?In the ViewCall and ViewBase classes, it would be better if I could put this.parse() in the ViewBase class in such a manner that it get called in the moment ViewClass was instatiated. But I couldn't find a way to do it. Does anyone have an idea on how I could do this?EditI improved the code again and created another question (the final one):Simple object-oriented calculator - part 4
Simple object-oriented calculator - part 3
javascript;object oriented;calculator
Current DesignThe implementation you have here uses less code than before and has readability for those who are accustomed to JavaScript. The simplest way to add readability, say for you blog, would be to add helper functions. But, here is where you must be careful, because you may add too many to the point where you create your own syntax. You can start to see that from what I made below pertaining to the superioritize function where it presents its own mini API for interfacing with the chain of overridden methods.My AttemptI tried to pull everything together and have some OOP looking concepts present.InheritThe first is inherit_to_from:function inherit_to_from(child, parent) {     child.prototype = new parent();     return child;}I know what you are thinking... He is a genius! (JK)The point of this function was merely to make it easier for when doing inheritance that it looks somewhat straight forward. Also, made the child first to look closer to what _C++ does. When I use it I place it before the declaration of the child that way it looks closer to C++.function Parent() {};inherit_to_from(Child, Parent);function Child() {};OverridingFor the overriding, I left the Base class such that every class used will have the same basic functions.// To make things easier added this function.function is_function(obj) { return obj instanceof Function };function Base() {};// Inheritable property 'override' used to handle overriding methods.Base.prototype.override = function(method, body) {    // Made it a little bullet proof.    if(is_function(this[method]) && is_function(body)) {        var overridden = this[method];        body.superior  = overridden;        this[method]   = body;    }    return this;};// Inheritable property 'def_prop' used to add some OOP.// This way instances can go  instance.def_prop(...) // rather than  Object.defineProperty(instance, ...) Base.prototype.def_prop = function(property, descriptor) {    Object.defineProperty(this, property, descriptor);    return this;};Now the tricky part was when you override the method, the context of the superior method becomes the method that had overridden it.  var base = new Base(); base.foo = function() { return this }; base.override(foo, base.foo); console.log(base.foo.superior() === base);     // => false console.log(base.foo.superior() === base.foo); // => trueSo, I created this strange fix which probably is not the best take but it keeps somewhat of the original design.// A helper function for determining if an object is SuperiorChainer (Not bullet proof...)function is_SuperiorChainer(obj) { return is_function(obj) && _SuperiorChainer.prototype === obj.prototype };// Idea would be to make this private...function _SuperiorChainer(context, propertyObject) {    var chain = function() {        return propertyObject.apply(context, arguments);    };    // Used to help with verification to whether it is a _SuperiorChainer.    chain.prototype = _SuperiorChainer.prototype;    Object.defineProperty(chain, context, { value: context });    // Gets the real object... Not the best name...    Object.defineProperty(chain, real, { value: propertyObject });    // Creates the links to the chain.    Object.defineProperty(chain, superior,         propertyObject.superior === undefined ?         { value: undefined } :         {             get: function() {                return _SuperiorChainer(context, propertyObject.superior);            }        }    );    return chain;}// The public function that starts the chain. (What an amazing name... *sarcasm*)function superioritize(context, propertyName) {    if(is_function(context[propertyName]))    return _SuperiorChainer(context, context[propertyName]);}The use is a little strange, but it works... var base = new Base(); base.foo = function() { return this }; base.override(foo, base.foo); console.log(superioritize(base, foo).superior() === base);     // => true console.log(superioritize(base, foo).superior() === base.foo); // => falseWhat you can do is edit the override function connected to Base to make it create the chain similar to what superioritize does.EDITSo, after revisiting this... I realized I could have just used bind to handle this problem... So, the new override function would look like this:// Inheritable property 'override' used to handle overriding methods.Base.prototype.override = function(method, body) {    // Made it a little bullet proof.    if(is_function(this[method]) && is_function(body)) {        var overridden = this[method];        body.superior  = overridden.bind(this);        this[method]   = body;    }    return this;};And it looks like it did before!!! var base = new Base(); base.foo = function() { return this }; base.override(foo, base.foo); console.log(base.foo.superior() === base);     // => true console.log(base.foo.superior() === base.foo); // => falseViewBaseFor this I created a unique function that will automatically define the camel case property and create the JQuery object.inherit_to_from(ViewBase, Base);function ViewBase() {};ViewBase.prototype.def_view_prop = function(property, id) {    this[property]              = id;    this[_.camelCase(property)] = $(# + id);    return this;};ViewCalcDid not really have to do anything for this except use the def_view_prop function. I did not attach the properties to the prototype. So, you cannot inherit from ViewCalc and have those properties be unique. Essentially, all objects of the inherited class would have the same values as to when the inheritance was made (an instance of ViewCalc was placed into the prototype so anything going down the prototype chain would get the same values).inherit_to_from(ViewCalc, ViewBase);function ViewCalc() {    this.def_view_prop(IPT_X,        'x');    this.def_view_prop(IPT_Y,        'y');    this.def_view_prop(IPT_RES,      'res');    this.def_view_prop(BTN_SUM,      'sum');    this.def_view_prop(BTN_SUBTRACT, 'subt');    this.def_view_prop(BTN_MULTIPLY, 'mult');    this.def_view_prop(BTN_DIVISION, 'div');    this.def_view_prop(BTN_CLEAN,    'clean');    this.def_view_prop(BTN_RAND,     'rand');};NOTE: I probably should have added another function of some sort that would update the JQuery objects in case they do not get loaded correctly or the HTML was to be dynamically created later (or something weird like that). But, instead what I did was just place the script at the end of the HTML file to make sure all of the HTML elements had been created before the JQuery objects attempted to be created.Operands & RandomizerFor Operands I added two smart getters and setters x and y. These allow you to directly set x and y which will automatically set the input. Also, this allows you to always get the most recent values in the inputs.inherit_to_from(Operands, Base);function Operands() {};// Because this is in the prototype, all share the same ViewCalc.Operands.prototype.def_prop(view, { value: new ViewCalc() });Operands.prototype.def_prop(x, {     get: function() { return +this.view.iptX.val() },    set: function(v) { this.view.iptX.val(v) },    enumerable: true});Operands.prototype.def_prop(y, {     get: function() { return +this.view.iptY.val() },    set: function(v) { this.view.iptY.val(v) },    enumerable: true});Operands.prototype.clean = function() {    this.x = 0;    this.y = 0;    return this;};inherit_to_from(Randomizer, Operands);function Randomizer() {};Randomizer.prototype.def_prop(randomize, { value: function() {    this.x = Math.round(Math.random() * 1000);    this.y = Math.round(Math.random() * 1000);    return this;}});OperationsFor Operations I merely copied and pasted what you had then made some minor changes.inherit_to_from(Operations, Randomizer);function Operations() {    var self = this;    function _doSum() {        return self.x + self.y;    };    function _doSubtraction() {        return self.x - self.y;    };    function _doMultiplication() {        return self.x * self.y;    };    function _doDivision() {        return self.x / self.y;    };    function _showRes(val) {        self.view.iptRes.val(val);    };    self.sum = function() {        _showRes(_doSum());    };    self.subtract = function() {        _showRes(_doSubtraction());    };    self.multiply = function() {        _showRes(_doMultiplication());    };    self.division = function() {        _showRes(_doDivision());    };     self.override(clean, function() {        superioritize(self, clean).superior();        self.view.iptRes.val();    });    self.view.btnSum.on     ('click', self.sum);    self.view.btnSubtract.on('click', self.subtract);    self.view.btnMultiply.on('click', self.multiply);    self.view.btnDivision.on('click', self.division);       self.view.btnClean.on   ('click', self.clean);    self.view.btnRand.on    ('click', function() { self.randomize() });};Code Inactionfunction is_function(obj) { return obj instanceof Function };function inherit_to_from(child, parent) {    child.prototype = new parent();    return child;};function _is_SuperiorChainer(obj) { return is_function(obj) && _SuperiorChainer.prototype === obj.prototype }function _SuperiorChainer(context, propertyObject) {    var chain = function() {        return propertyObject.apply(context, arguments);    };    chain.prototype = _SuperiorChainer.prototype;    Object.defineProperty(chain, context, { value: context });    Object.defineProperty(chain, real, { value: propertyObject });    Object.defineProperty(chain, superior,         propertyObject.superior === undefined ?         { value: undefined } :         {             get: function() {                return _SuperiorChainer(context, propertyObject.superior);            }        }    );    return chain;}function superioritize(context, propertyName) {    if(is_function(context[propertyName]))    return _SuperiorChainer(context, context[propertyName]);}function Base() {}Base.prototype.override = function(method, body) {    if(is_function(this[method]) && is_function(body)) {        var overridden = this[method];        body.superior  = overridden;        this[method]   = body;    }    return this;};Base.prototype.def_prop = function(property, descriptor) {    Object.defineProperty(this, property, descriptor);    return this;};inherit_to_from(ViewBase, Base);function ViewBase() {};ViewBase.prototype.def_view_prop = function(property, id) {    this[property]              = id;    this[_.camelCase(property)] = $(# + id);    return this;};inherit_to_from(ViewCalc, ViewBase);function ViewCalc() {    this.def_view_prop(IPT_X,        'x');    this.def_view_prop(IPT_Y,        'y');    this.def_view_prop(IPT_RES,      'res');    this.def_view_prop(BTN_SUM,      'sum');    this.def_view_prop(BTN_SUBTRACT, 'subt');    this.def_view_prop(BTN_MULTIPLY, 'mult');    this.def_view_prop(BTN_DIVISION, 'div');    this.def_view_prop(BTN_CLEAN,    'clean');    this.def_view_prop(BTN_RAND,     'rand');};inherit_to_from(Operands, Base);function Operands() {};// Because this is in the prototype all have the same ViewCalc.Operands.prototype.def_prop(view, { value: new ViewCalc() });Operands.prototype.def_prop(x, {     get: function() { return +this.view.iptX.val() },    set: function(v) { this.view.iptX.val(v) },    enumerable: true});Operands.prototype.def_prop(y, {     get: function() { return +this.view.iptY.val() },    set: function(v) { this.view.iptY.val(v) },    enumerable: true});Operands.prototype.clean = function() {    this.x = 0;    this.y = 0;    return this;};inherit_to_from(Randomizer, Operands);function Randomizer() {};Randomizer.prototype.def_prop(randomize, { value: function() {    this.x = Math.round(Math.random() * 1000);    this.y = Math.round(Math.random() * 1000);    return this;}});inherit_to_from(Operations, Randomizer);function Operations() {    var self = this;    function _doSum() {        return self.x + self.y;    };    function _doSubtraction() {        return self.x - self.y;    };    function _doMultiplication() {        return self.x * self.y;    };     function _doDivision() {        return self.x / self.y;    };        function _showRes(val) {        self.view.iptRes.val(val);    };        self.sum = function() {        _showRes(_doSum());    };    self.subtract = function() {        _showRes(_doSubtraction());    };    self.multiply = function() {        _showRes(_doMultiplication());    };    self.division = function() {        _showRes(_doDivision());    };     self.override(clean, function() {        superioritize(self, clean).superior();        self.view.iptRes.val();    });    self.view.btnSum.on     ('click', self.sum);    self.view.btnSubtract.on('click', self.subtract);    self.view.btnMultiply.on('click', self.multiply);    self.view.btnDivision.on('click', self.division);       self.view.btnClean.on   ('click', self.clean);    self.view.btnRand.on    ('click', function() { self.randomize() });};var o = new Operations();<script src = https://cdnjs.cloudflare.com/ajax/libs/lodash.js/3.10.1/lodash.min.js></script><script src = https://ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min.js></script><body>X: <input id='x'><br>Y: <input id='y'><br>Res: <input id='res'><br><input id='sum' type='button' value='+'><input id='subt' type='button' value='-'><input id='mult' type='button' value='*'><input id='div' type='button' value='/'><input id='clean' type='button' value='C'><input id='rand' type='button' value='Rand'></body><!-- <script src = test.js></script> -->