Nelson Teixeira
Nelson Teixeira

Reputation: 6562

Several formats of using OOP in JS

Some time ago I was tying to understand js oop coming from many years of oop coding in several other laguages. I found it very confusing. After some months, a JS book and many articles and SO questions read, I sumarized it all in a concrete example that ilustrated the main oop concepts correctly working. Here it is:

function Operators() {
    //mandatory
    var self = this

    //private
    var IPT_X = '#x'
    var IPT_Y = '#y'

    //public
    this.x = 0
    this.y = 0    

    this.showOperators = function() {
    //use of a private property (IPT_X) and a public property (this.x)
    $(IPT_X).val(this.x)
    $(IPT_Y).val(this.y)
    }

    this.clean = function() {
    this.x = 0
    this.y = 0
    // call to a local public method 
    this.showOperators()
    }

    this.updateOperators = function(_x, _y) {
    // use of a public property when call from
    // derived class method is necessary
    self.x = _x
    self.y = _y
    }
}

function Randomizer() {
    // mandatory for derived classes
    Operators.call(this)
    // mandatory for overloaded methods with call to the inherited method
    var parentUpdateOperators = this.updateOperators
    var self = this

    // private
    function getRandomNumber() {
    return Math.round(Math.random() * 1000)
    }

    // public
    this.updateOperators = function(_x, _y) {
        // call to inherited method of superior class
        parentUpdateOperators(_x, _y)
        // call to method of superior class
        self.showOperators()
    } 

    this.populateRandomNumbers = function() {
    // call to public local method (this.updateOperators())
    // and to a local private method (getRandomNumber())
    this.updateOperators(getRandomNumber(), getRandomNumber())
    }

    // init
    this.populateRandomNumbers()
}
// Mandatory for derived classes. Allows access to superior classes with
// more than 2 levels of inheritance ("grandfather" classes)
Randomizer.prototype = Object.create(Operators.prototype)

function Operations() {
    Randomizer.call(this)
    var self = this

    //private
    var IPT_RES = '#res'
    var BTN_SUM =  '#sum'
    var BTN_SUBTRACT =  '#subt'
    var BTN_MULTIPLY =  '#mult'
    var BTN_DIVISION =  '#div'
    var BTN_CLEAN =  '#clean'
    var BTN_RAND =  '#rand'

    function calcSum() {
    return self.x + self.y
    } 
    function calcSubtraction() {
    return self.x - self.y
    } 
    function calcMultiplication() {
    return self.x * self.y
    } 
    function calcDivision() {
    return self.x / self.y
    } 

    function showRes(val) {
    $(IPT_RES).val(val)
    }

    //public
    this.sum = function() {
    // call to 2 local private methods
    showRes(calcSum())
    }
    this.subtract = function() {
    showRes(calcSubtraction())
    }
    this.multiply = function() {
    showRes(calcMultiplication())
    }
    this.division = function() {
    showRes(calcDivision())
    }

    // init
    $(BTN_SUM).on('click', function() { self.sum() })
    $(BTN_SUBTRACT).on('click', function() { self.subtract() })
    $(BTN_MULTIPLY).on('click', function() { self.multiply() })
    $(BTN_DIVISION).on('click', function() { self.division() })    
    $(BTN_CLEAN).on('click', function() { self.clean() })
    $(BTN_RAND).on('click', function() { self.populateRandomNumbers() })
}
Operations.prototype = Object.create(Randomizer.prototype)

var obj = new Operations()

and here is the necessary HTML to make it work:

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'>

Here is a JSFiddle with my example working fine:

http://jsfiddle.net/vqqrf2cb/24/

Then I started to use this format in my daily work and all is working good. But now, after some months successfully using this format, I'm continuing to read about this subject and see that many people use different formats. So I'm trying to adapt the code above to the other possible formats of using objects. The ones I know are:

Format 1:

var something = (function() {
     //private
     foo = 111
     bar = 222
     function baz() {
    //whatever
     }
     return {
    // public
    x : 333,
    y : 444,
    z : function() {
        // whatever
    }
     }
})()

Format 2:

var something = (function() {
     var obj {
         foo : 111,
         bar : 222,
         function baz() {
        //whatever
         },
     }
     return obj
})()

Format 3:

var something = {
     foo : 111,
     bar : 222,
     baz : function() {
    //whatever
     }
}

Format 4:

// define the Person Class
function Person() {}

Person.prototype.walk = function(){
  alert ('I am walking!');
};
Person.prototype.sayHello = function(){
  alert ('hello');
};

// define the Student class
function Student() {
  // Call the parent constructor
  Person.call(this);
}

// inherit Person
Student.prototype = new Person();

// correct the constructor pointer because it points to Person
Student.prototype.constructor = Student;

// replace the sayHello method
Student.prototype.sayHello = function(){
  alert('hi, I am a student');
}

// add sayGoodBye method
Student.prototype.sayGoodBye = function(){
  alert('goodBye');
}

And many small variations of all these formats. I'm even inclined to think the ways of doing OOP in JS tend to infinity. :D Anyway, I want to try some of the methods to find which one I find better in day-to-day work. But I couldn't convert my code to any other format in such a way to make all oop work. By 'all oop' I mean encapsulation (private and public methods and variables), inheritance, overloading with superior class call and finally class initialization (like a constructor or something).

My question is: Can somebody translate my working code to some other js object formats ? I'm specially interested in converting to Format 1 and Format 3. Other formats not included in the list are welcome also. One restriction: I don't want to use mixins. I want to use prototypes for inheritance.

I also have read about the modular pattern. How would this example be like using it ?

Corrections/Comments to my example are welcome also.

Edit:

After the answer and comments of @Robert and @NoBugs I've made an alternative example using the Module Pattern:

http://jsfiddle.net/ehe122e0/10/

Upvotes: 0

Views: 106

Answers (1)

Robert
Robert

Reputation: 677

JavaScript has many choices, so it confused me for quite a while, but ultimately I ended up embracing it as something new and disregarding any OOP knowledge I had.

For example, take Format 1 and 2 you described above. This isn't equal to classes in other languages, but is similar in the fact that it encapsulates some "thing" in your program. You should always wrap your code in at least one function to ensure your variables are never in the global scope to avoid conflicting names with other programmers. Look up "IIFE" for more details.

I generally create my objects that are simply there to move data amongst my application using the object literal notation.

var student = {
    firstName: "Bob",
    age: 20
};

And stick to prototypical inheritance if my objects will need any functions like Format 4 because it offers more flexibility.

Regardless of what format you decide to follow, an IIFE is important. And forget about the word "Class" and embrace the modular pattern, it is very similar but different. It's like trying to use Git like SVN.

Here is an example of a printModule and a module making use of it. Notice ModuleB can replace the current printModule with another one easily simply by passing a different object into moduleB. The way I use the exports variables varies depending on the person.

http://jsfiddle.net/0zuo1w4d/1/

Upvotes: 1

Related Questions