View Full Version : Complete OOP with JavaScript and Ext JS. Private, protected and public methods
flowerlin
15 Dec 2008, 7:41 AM
Hello to all,
Take a look at this article http://dreamix.eu/blogix/index.php/2008/12/12/complete-object-oriented-programming-with-javascript-and-ext-js/ describing how to create JavaScript classes with private, public and protected (final and non-final) methods and how to extend them in order to create Java style type hierarchy.
Animal
15 Dec 2008, 8:22 AM
This is not good:
Dreamix.Base = function() {
this.publicMethod = function(param) {
alert(”Dreamix.Base#publicMethod:” + param);
};
Dreamix.Base.prototype.publicMethod \\
= this.publicMethod;
}
So for every instance, you create a new publicMethod function local to the instance, and use that new function to overwrite any reference the prototype may have had to that? Why would you do this?
mabello
15 Dec 2008, 8:48 AM
You can get rid of the last part:
Dreamix.Base.prototype.publicMethod \\
= this.publicMethod;
jay@moduscreate.com
15 Dec 2008, 9:04 AM
I dislike how people *still* use alerts.
flowerlin
15 Dec 2008, 9:19 AM
When we want instance method, it is normal to create it every time we create an instance. Otherwise it will be static.
We assign this public method to Dreamix.Base.prototype.publicMethod in order to make the inheritance to work properly. If we don't do this, we cannot call this method in the derived class (e.g call to super method). Dreamix.DerivedClass.superclass.publicMethod will be 'null or not an object'.
flowerlin
15 Dec 2008, 9:24 AM
I dislike how people *still* use alerts.
If you have better ideas to make the examples more sympathetic, I'll be glad if you share them and I'll change the examples. For me, this alert is like System.out.println() in Java - the easiest way to show something.
mabello
15 Dec 2008, 10:13 AM
This is what I used to "use", it's old code but it does its job (hence my previous post, I'm sorry I didn't finish to read your linked article, I'll do that though):
Utility = {};
Utility.isFunction = function(a){
return (typeof a == 'function');
}
Utility.isObject = function(a){
return (typeof a == 'object' && !!a) || Utility.isFunction(a);
}
Utility.protoInherits = function(baseClass, subClass)
{
if(!Utility.isObject(baseClass)) {
throw '!!Utility.isObject(baseClass)';
}
if(!Utility.isObject(subClass)){
throw '!Utility.isObject(subClass)';
}
if(baseClass.constructor == Function)
{
//Normal Inheritance
subClass.prototype = new baseClass();
subClass.prototype.constructor = subClass;
subClass.prototype.superClass = subClass.prototype;//Not used
}
else
{
//Pure Virtual Inheritance
subClass.prototype = baseClass;
subClass.prototype.constructor = subClass;
subClass.prototype.superClass = baseClass;//Not used
}
}
MyNameSpace = {};
MyNameSpace.Person = function(name) {
this.name = name;
this.getName = function(){
return this.name;
}
this.toString = function(){
return "My Name is " + this.getName();
}
}
MyNameSpace.Student = function(name, id) {
//Call the super constructor
MyNameSpace.Person.call(this, name);
this.id = id;
this.getId = function(){
return this.id;
}
this.toString = function(){
//Call the super toString
return MyNameSpace.Student.prototype.toString.call(this) + " and my id is " + this.getId();
}
}
Utility.protoInherits(MyNameSpace.Person, MyNameSpace.Student);
var person = new MyNameSpace.Person("Marco");
var student = new MyNameSpace.Student("Luca", "123");
alert(person.toString());
alert(student.toString());
That's how I was emulating inheritance in javascript about 3/4 years ago...I'm sadly getting old!
Animal
15 Dec 2008, 10:38 AM
When we want instance method, it is normal to create it every time we create an instance. Otherwise it will be static.
We assign this public method to Dreamix.Base.prototype.publicMethod in order to make the inheritance to work properly. If we don't do this, we cannot call this method in the derived class (e.g call to super method). Dreamix.DerivedClass.superclass.publicMethod will be 'null or not an object'.
No, it's not normal.
It's normal to put it into the class's prototype once.
Then it can be called from anywhere.
flowerlin
15 Dec 2008, 11:12 AM
This is what I used to "use", it's old code but it does its job (hence my previous post, I'm sorry I didn't finish to read your linked article, I'll do that though):
Utility = {};
Utility.isFunction = function(a){
return (typeof a == 'function');
}
Utility.isObject = function(a){
return (typeof a == 'object' && !!a) || Utility.isFunction(a);
}
Utility.protoInherits = function(baseClass, subClass)
{
if(!Utility.isObject(baseClass)) {
throw '!!Utility.isObject(baseClass)';
}
if(!Utility.isObject(subClass)){
throw '!Utility.isObject(subClass)';
}
if(baseClass.constructor == Function)
{
//Normal Inheritance
subClass.prototype = new baseClass();
subClass.prototype.constructor = subClass;
subClass.prototype.superClass = subClass.prototype;//Not used
}
else
{
//Pure Virtual Inheritance
subClass.prototype = baseClass;
subClass.prototype.constructor = subClass;
subClass.prototype.superClass = baseClass;//Not used
}
}
MyNameSpace = {};
MyNameSpace.Person = function(name) {
this.name = name;
this.getName = function(){
return this.name;
}
this.toString = function(){
return "My Name is " + this.getName();
}
}
MyNameSpace.Student = function(name, id) {
//Call the super constructor
MyNameSpace.Person.call(this, name);
this.id = id;
this.getId = function(){
return this.id;
}
this.toString = function(){
//Call the super toString
return MyNameSpace.Student.prototype.toString.call(this) + " and my id is " + this.getId();
}
}
Utility.protoInherits(MyNameSpace.Person, MyNameSpace.Student);
var person = new MyNameSpace.Person("Marco");
var student = new MyNameSpace.Student("Luca", "123");
alert(person.toString());
alert(student.toString());
That's how I was emulating inheritance in javascript about 3/4 years ago...I'm sadly getting old!
Yes, this one looks better since you don't have to (re)initialize MyNameSpace.Person.prototype.toString in order to make the inheritance to work properly. Unfortunately (or not), Ext.extends(...) requires it and I didn't want to write my own function for inheritance.
Two notes:
- It will be better if in this example we can use 'var id' instead of 'this.id'. Then we can achieve some form of encapsulation.
- It will be better if we can call MyNameSpace.Student.superclass.constructor.call(this, name) (or something similar) instead of MyNameSpace.Person.call(this, name);. Then if we change the name of the base class, we don't have to change all its derived classes.
flowerlin
15 Dec 2008, 11:40 AM
No, it's not normal.
It's normal to put it into the class's prototype once.
Then it can be called from anywhere.
I guess you didn't read it right. It is normal to create an instance method every time we create an instance from a type.
The thing that is not normal here, is that we have to use this ugly code in order to make the Ext.extends(...) function working:
Dreamix.Base.prototype.publicMethod = this.publicMethod;If we do like this:
[php]Dreamix.Base.prototype.publicMethod = function(param) {
alert(
Animal
16 Dec 2008, 1:27 AM
No. The prototype consists of the anonymous functions and Ext.extend is quite correct. Public methods of a class go in the prototype
You should not assign new functions in a constructor using this.foo = function(){...}
There's enough bad practice being encouraged here enough as it is!
mabello
16 Dec 2008, 1:47 AM
I agree with you Animal but:
You should not assign new functions in a constructor using this.foo = function(){...}
I don't see what's the big deal about that, I don't say it's a best practice, but if you know what you are doing you can do that.
Instead of using the prototype, you use the constructor function(in my case MyNameSpace.Person) to setup the public methods for all the instance created from that "constructor function"; yes it is less flexible and efficient and not exactly what I recommend to do as well but there's nothing confusing about that, at least for me.
I do want to say though that OO is not only about inheritance, but it's should also be al least about encapsulation of course, and to be honest, also in Ext this is not achieved at all.
In the previous example, you can achieve encapsulation in a very easy way using closures:
MyNameSpace.Person = function(theName) {
var name = theName;//name is private now
this.getName = function(){
return name;
}
this.toString = function(){
return "My Name is " + this.getName();
}
}
Now I don't suggest to use my snippet and my extend method, stick with Ext.extend and use the prototype for all your public methods as Animal suggest.
Something that it's not great in ext is that you understand if something is private because there is a comment "private" before the function.
If someone in C# or Java code write a public method in a class with a comment "private" before it ,saying that you should not use it because is private, well I could faint :)
flowerlin
16 Dec 2008, 4:09 AM
No. The prototype consists of the anonymous functions and Ext.extend is quite correct. Public methods of a class go in the prototype
You should not assign new functions in a constructor using this.foo = function(){...}
There's enough bad practice being encouraged here enough as it is!
I think you should read the article more carefully. There I explain why I do this.
As I said, I tried without the prototype assignment and it is not working, because superclass.prototype.publicMethot does not exist. If you know other way to call the base method, share it, don't just say 'there's enough bad practice being encouraged here enough as it is'. I'll be glad, if we remove these 'bad practices' it will be good for the end user.
mabello
16 Dec 2008, 4:25 AM
The best practice Animal is talking about is this:
//Constructor
MyNameSpace.Person = function(name) {
this.name = name;
}
//Use prototype for the public methods etc
MyNameSpace.Person.prototype = {
getName: function(){
return this.name;
},
toString : function(){
return "My Name is " + this.getName();
}
}
I think that if you modify your code and you stick this way, Ext extend should work as expected.
Animal
16 Dec 2008, 4:26 AM
I think you should read the article more carefully. There I explain why I do this.
As I said, I tried without the prototype assignment and it is not working, because superclass.prototype.publicMethot does not exist
Ext.extend sets the superclass property to be the prototype.
read the Ext source code!
Sio the Ext.Panel class calls its superclass's onRender like this:
Ext.Panel.superclass.onRender.call(this, ct, position);
flowerlin
16 Dec 2008, 4:39 AM
The best practice Animal is talking about is this:
//Constructor
MyNameSpace.Person = function(name) {
this.name = name;
}
//Use prototype for the public methods etc
MyNameSpace.Person.prototype = {
getName: function(){
return this.name;
},
toString : function(){
return "My Name is " + this.getName();
}
}
I think that if you modify your code and you stick this way, Ext extend should work as expected.
Yes, it will work. The point here is that I don't want to define this.name. I want var name so I can achieve the encapsulation. Why you define this.name and getName? You can do:
var person = new MyNameSpace.Person('some name');
person.name = 'new name';
Here the OOP paradigm about encapsulation breaks.
If you define var name, the only way to get the name is to use the instance public method. With the 'best practice' mentioned above, you cannot define var name and use it outside the scope of the constructor. If you use scoped variable person.name will return 'undefined'.
mabello
16 Dec 2008, 4:44 AM
Fair enough.
This is definitely a good point, and I agree about that, as you can read in one of my previous post.
Animal
16 Dec 2008, 4:47 AM
It's just a gotcha of current versions of ECMAScript. No truly private variables without horrible hacks.
flowerlin
16 Dec 2008, 5:08 AM
Ext.extend sets the superclass property to be the prototype.
read the Ext source code!
Sio the Ext.Panel class calls its superclass's onRender like this:
Ext.Panel.superclass.onRender.call(this, ct, position);
I read it. And in the examples in the article I use this way. The problem is that:
1. If we don't define the prototype, we don't have the superclass => we cannot call the base implementation;
2. So we need to define the prototype: There are 2 ways: inside the constructor scope, and outside. If we defined it outside we cannot use scoped variables. So we need to defined it inside. Because it is inside the constructor, every time we create an object (we call the constructor) we change the prototype. So if we don't use this.publicMethod, but prototype.publicMethod, all instances of this type will have the same method reference and when we call publicMethod of an instance we will call the method of the last created instance. Example:
/**
* @author Cvetelin Andreev, Dreamix Ltd. http://www.dreamix.eu
* @since 11/11/2008
*/
Ext.namespace('Dreamix');
Dreamix.Base = function(param) {
var privateMember = param;
var privateFunction = function() {
alert("Dreamix.Base#privateFunction:" + privateMember);
}
Dreamix.Base.prototype.publicMethod = function() {
privateFunction();
};
}
var base = new Dreamix.Base('base');
var base1 = new Dreamix.Base('base1');
base.publicMethod();
base1.publicMethod();
The result will be:
Dreamix.Base#privateFunction:base1
Dreamix.Base#privateFunction:base1
As you can see, both times it prints base1, which means that we call the same method.
Animal
16 Dec 2008, 5:12 AM
Of course you can use scoped variables if you define the prototype outside of the constructor. I don't know why you are creatnig such convoluted schemes.
flowerlin
16 Dec 2008, 5:17 AM
Of course you can use scoped variables if you define the prototype outside of the constructor. I don't know why you are creatnig such convoluted schemes.
If I do this:
/**
* @author Cvetelin Andreev, Dreamix Ltd. http://www.dreamix.eu
* @since 11/11/2008
*/
Ext.namespace('Dreamix');
Dreamix.Base = function(param) {
var privateMember = param;
var privateFunction = function() {
alert("Dreamix.Base#privateFunction:" + privateMember);
}
}
Dreamix.Base.prototype.publicMethod = function() {
privateFunction();
};
It gives me an error 'privateFunction is not define'
Animal
16 Dec 2008, 5:20 AM
OK, I take it you are talking about lexical scope. as opposed to a "this" reference. Yes, obviously to have access to defined vars in a constructor, a function has to be defined in that lexical scope.
But it's a horrible hack just to get private variables and results in new functions for each instance. It's a waste of resources for no reward. You get private variables, but so what? Why not just use discipline within the dev team?
flowerlin
16 Dec 2008, 5:55 AM
OK, I take it you are talking about lexical scope. as opposed to a "this" reference. Yes, obviously to have access to defined vars in a constructor, a function has to be defined in that lexical scope.
But it's a horrible hack just to get private variables and results in new functions for each instance. It's a waste of resources for no reward. You get private variables, but so what? Why not just use discipline within the dev team?
It is not me, who invented to paradigm of private variables. The discipline is a good thing, but not everyone has it, and the private variables are a way to restrict the user of your classes, no matter if has discipline, or not. You cannot be sure that if you define your member like this.member and comment it //don't use this variable somebody will follow this advice.
About the resources issue, I'm not very familiar with it. I'll make some tests for memory and performance and I'll post them here. Until then, if you know something more about it you can contribute again.
I know that this article is not what the poeple are used to read. It tries to apply Java style inheritance to JavaScript. Which may be wrong approach. But when I program, I prefer to have private, protected and public methods, because this is the way I'm used to ... And I lite it.
If there are no performance or memory issues, I think the strategy is good enough to use it in a real life application.
Animal
16 Dec 2008, 6:09 AM
Performance issues? I would have thought it was obvuious.
Every time you call your constructor, you create a whole new set of member functions.
This:
var privateFunction = function() {
alert("Dreamix.Base#privateFunction:" + privateMember);
}
Is an executable statement which creates a new function.
If you have several of these "member functions", and you instantiate several of these classes, you clog up the memory with identical functions just to get round the fact that the current versin of ECMAscript does not have private members.
Powered by vBulletin® Version 4.1.5 Copyright © 2012 vBulletin Solutions, Inc. All rights reserved.