Reputation: 6562
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
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