PDA

View Full Version : Help with OOP javascript



pman
05-26-2007, 12:49 AM
Hi there,

I'm trying to create a class and having trouble calling a public / privileged method from the class constructor. Here's the example:


function aClassName(param1, param2)
{
//constructor begin

this.var1 = param1 ;
this.var2 = param2 ;

this.doSomething() ;

//constructor end

//privileged function

this.doSomething = function()
{
alert("I'm doing something...") ;
}
}

Now, when I try to create a new object of this class,

var myObject = new aClassName("ab", "cd") ; the code gets stuck at
this.doSomething() ; which is being called from the constructor.

But if I comment that line and do the following, then everything goes okay.

var myObject = new aClassName("ab", "cd") ;
myObject.doSomething() ;


So, I guess, my question is, how can I call a privileged/public function from the constructor?

Your help is much appreciated. Thank you so much.


__________________
Regards,

Pman
http://www.pmansLab.com

Trinithis
05-26-2007, 01:03 AM
function ClassAndConstructor(a, b, c) {
//public variables
this.prop1 = a;
this.prop2 = b;
this.prop3 = b*c;
this.prop4 = 89;
this.prop5 = new Array();
this.prop5[0] = "foo";
this.prop5[1] = {prop1: "gr347!"}

//private variable
var priv1 = "private!";

//methods
this.method1 = function(a) {
return a*this.prop3;
}
this.method2 = function(a) {
this.prop1 = a;
}
this.method3 = function() {
return priv1;
}
this.method4 = function() {
return privateMethod1();
}

//private method
function privateMethod1() {
return "from privateMethod1";
}
}
var coolestObectEver = new ClassAndConstructor("interestingString", 4, 11);
alert(coolestObectEver.prop3); //alerts 44
alert(coolestObectEver.method1(2)); //alerts 88
alert(coolestObectEver.prop3); //alerts 44
alert(coolestObectEver.prop5[1].prop1); //alerts "gr347!"
alert(coolestObectEver.method3()); //alerts "private!"
alert(coolestObectEver.method4()); //alerts "from privateMethod1"


I just included both public and private variables and methods, but (almost all) scripts don't really need to stoop to the level of making private stuff for objects.

*EDIT*
I misread your post. In answer, just move the calling of this.doSomething() after you define this.doSomething.

pman
05-26-2007, 01:19 AM
Hi Trinithis,

Thank so much for your reply. Actually my problem was something else and I don't think your example addressed it at all. what I would like is that, when you write

var coolestObectEver = new ClassAndConstructor("interestingString", 4, 11);

method1(a) should be called right away after setting all the properties.

So, basically, in your class / object, there should be a method that users will be able to call at any time like you did by saying

coolestObectEver.method1(2)

and that method1 should also be called automatically by the constructor when you create the object by saying

var coolestObectEver = new ClassAndConstructor("interestingString", 4, 11);

Let me know, if my question is not clear.

Thanks

________________

Regards

Pman
http://www.pmansLab.com/

pman
05-26-2007, 02:45 AM
*EDIT*
I misread your post. In answer, just move the calling of this.doSomething() after you define this.doSomething.

Thanks Trinithis. That solved the problem. Was fighting with this for so long. Thanks so much.

Trinithis
05-26-2007, 04:47 AM
So, basically, in your class / object, there should be a method that users will be able to call at any time like you did by saying

coolestObectEver.method1(2)

and that method1 should also be called automatically by the constructor when you create the object by saying

var coolestObectEver = new ClassAndConstructor("interestingString", 4, 11);

The method does not need to be called by the constructor when the object is created. If you want do do something to that effect (like in your example), then yeah, call the method within the constructor after the method is defined.

Twey
05-26-2007, 06:05 PM
I'd disagree with the use of "private" data, though. I take a Pythonic approach to this: if something shouldn't be changed, the programmer using it should know that it shouldn't be changed. It shouldn't be inaccessible, because this limits the flexibility of the code. In ECMAScript there's another incentive to make everything public: if you use a closure to allow access to certain data only via privileged methods, that closure is recreated for each instance of the class instead of only once for the prototype, leading to a dramatic decrease in performance.

pman
05-26-2007, 08:31 PM
I'd disagree with the use of "private" data, though. I take a Pythonic approach to this: if something shouldn't be changed, the programmer using it should know that it shouldn't be changed. It shouldn't be inaccessible, because this limits the flexibility of the code. In ECMAScript there's another incentive to make everything public: if you use a closure to allow access to certain data only via privileged methods, that closure is recreated for each instance of the class instead of only once for the prototype, leading to a dramatic decrease in performance.

So, are you saying that it's better to use public methods instead of privileged method?

Trinithis
05-26-2007, 09:03 PM
Yeah, that's what he's saying, and I agree too. I just included them in my example because you explicitly mentioned "public" stuff, so I put in private types for more completeness.

pman
05-27-2007, 05:36 AM
Thanks so much for the reply. I'm still trying to get used to Closures in Javascript. I can probably do the basic closure, but I think I'll need more time & lesson for advanced usage of that. Regarding the performance issue, you might be right.

Since I'm not that experienced in OOP Javascript, I'm not sure about the difference between a privileged and public in javascript.


Originally Posted by Twey View Post
I'd disagree with the use of "private" data, though. I take a Pythonic approach to this: if something shouldn't be changed, the programmer using it should know that it shouldn't be changed. It shouldn't be inaccessible, because this limits the flexibility of the code.

I don't agree with that though. I don't think we should make everything public and assume that the user will be using things appropriately. I guess, it's debatable.

pman
05-27-2007, 05:49 AM
I need another help from you guys. Now I'm having trouble using setTimeout function inside the class. From my original example, this is what I did


function aClassName(param1, param2)
{
//privileged function

this.doSomething = function()
{
alert("I'm doing something...") ;
}

this.run = function()
{
setTimeout("this.doSomething()", 500) ;
}

//constructor begin

this.var1 = param1 ;
this.var2 = param2 ;

this.doSomething() ;

//constructor end

}

So, after creating a new object of this class if I execute the run method like this:


var myObj = new aClassName(1, 2) ;
myObj.run() ;

it doesn't work. After a lot of googling, it looks like "this" inside setTimeout doesn't refer to the actual object. It refers to the Window. From some of the suggestions, I tried the following:


function aClassName(param1, param2)
{
//privileged function

this.doSomething = function()
{
alert("I'm doing something...") ;
}

this.run = function()
{
setTimeout("self.doSomething()", 500) ;
}

//constructor begin

this.var1 = param1 ;
this.var2 = param2 ;

var self = this ;

this.doSomething() ;

//constructor end

}

But that didn't work either. Then I used closure and changed the "run" method like this:



this.run = function()
{
setTimeout(function() {this.doSomething() ; }, 500) ;
}


Is there any other solution to this problem? Thanks again.

Twey
05-27-2007, 01:08 PM
I don't agree with that though. I don't think we should make everything public and assume that the user will be using things appropriately. I guess, it's debatable.Why not? This is, after all, what documentation is for. My point on the issue is that it depends who you're coding for. If you're coding for a three-year-old chimpanzee who can't read the glaring line in the documentation that says "DO NOT MODIFY THIS VARIABLE AT ANY COST," or ignores the convention of prefixing "dangerous" members with an underscore (_), then you need to use private members. If you're assuming that anyone using your code will be a human being, it's safe to make it all public :)

The wisdom of creating private data is indeed debatable, but the performance hit in ECMAScript is not. An extreme case is flyweight objects. If you're unfamiliar with the flyweight pattern, one has a large pool of very small objects containing the bare minimum data required. These might be used to represent, for example, the characters in a word-processing program. In that particular example, it would be entirely reasonable to have 10,000+ such objects. In ECMAScript, this can be done quite adequately with entirely public data:
function Glyph(character) {
this.character = character;
}

Glyph.prototype.delete = function() {
doSomethingWith(this.character);
// Delete the glyph from the pool somehow, and do cleanup.
};There is only one instance of delete(): only the minimum data that needs to be stored (in this case, the character) is stored. However, if you decide to make character private for some reason:
function Glyph(character) {
this.delete = function() {
doSomethingWith(character);
// Delete the glyph from the pool somehow, and do cleanup.
};
}Every time you create a new Glyph instance, a new delete() function is created, and it and its whole scope are preserved until the Glyph instance is deleted. This has a very negative effect in the case of applications like the one I described. It is less of a problem, but still something to consider, with smaller applications with less objects.
After a lot of googling, it looks like "this" inside setTimeout doesn't refer to the actual object. It refers to the Window.Yes, if you pass a string to setTimeout(), it's evaluated at global scope. It's considered poor practice to do so for this reason and others.
this.run = function()
{
setTimeout(function() {this.doSomething() ; }, 500) ;
}this still points to window. Store the current object in a temporary scope:
this.run = function()
{
setTimeout(
(function(me) {
return function() {
me.doSomething();
me = null; // prevent memory leaks in IE if the object holds a reference to a DOM node.
// Can get trickier if using recursive setTimeout()s or setInterval(), where the reference must be reused.
};
})(this),
500
);
}

Trinithis
05-27-2007, 05:56 PM
The way I have solved this type of problem in the past is as follows:


this.run = function() {
setTimeout(function(o, a, b) {o.doSomething(a, b);}, 500, this, "arg a", "arg b");
}
//I added arguments to doSomething() so you can see how that works too.

I think this looks cleaner than Twey's, but perhaps I am missing something.

Twey
05-27-2007, 06:09 PM
That use of setTimeout() is, if I remember correctly, unreliable.

pman
05-27-2007, 07:18 PM
That use of setTimeout() is, if I remember correctly, unreliable.
why? What would you use then setInterval(). May be I should google to see the difference between this two.

Trinithis,
Doesn't setTimeout take only 2 arguments? I think I have seen more arguments being used, but it doesn't work in IE.( I haven't tried it out though. Just found it while googling. One of the sites are http://www.klevo.sk/category/javascript/)

Twey
05-27-2007, 07:41 PM
why? What would you use then setInterval(). May be I should google to see the difference between this two.setInterval() calls the function once every interval milliseconds, so it's not possible to just delete the object after it's finished doing its thing the first time. There's no easy way to solve this problem, and you just have to try to find some way to tell when the setInterval() has accomplished all it should have.

If it wasn't clear, I meant Trinithis' usage of setTimeout(), with the extra arguments.

pman
05-27-2007, 08:02 PM
Why not? This is, after all, what documentation is for. My point on the issue is that it depends who you're coding for. If you're coding for a three-year-old chimpanzee who can't read the glaring line in the documentation that says "DO NOT MODIFY THIS VARIABLE AT ANY COST," or ignores the convention of prefixing "dangerous" members with an underscore (_), then you need to use private members. If you're assuming that anyone using your code will be a human being, it's safe to make it all public

I am speaking in general, not for javascript only. I think things should be done properly. If a data needs to be private, it should be private and vice versa.
Also, sometimes you want to keep the data private to have more control over it. If you have a variable that needs to be integer for example, and the value that was given to it is something else, then you will need to do a lot of checking and casting later on whenever you will need to use that. And if an object was passed instead of integer, it's even worse. You can't do anything. I think this is why, encapsulation was put in place for OOP paradigm. You can still say that we are not programming for a chimpangee and I can't argue with that. :D



The wisdom of creating private data is indeed debatable, but the performance hit in ECMAScript is not.

I agree with you 100%. I think what you were trying to get at is that when we are using closure, then the entire scope is being preserved and obviously, it has negative effect in terms of using resources.

I am still a new learner about javascript clouser. Correct me if I'm wrong, but I think clouser should only be used in special cases and not all the time. If we have too much of clouser in a class, then it should probably be re-engineered. We also need to use it efficiently. And if we are not careful, ofcourse it's gonna have bad effect; just like anything else that exists. Things have both good effect and bad effect depending on how you use it.

Twey
05-27-2007, 11:29 PM
I am speaking in general, not for javascript only. I think things should be done properly. If a data needs to be private, it should be private and vice versa.Data (which is a plural or a mass noun, never a singular; the singular is "datum") never needs to be private. It's a mechanism designed to stop stupid people breaking their programs by accident.
Also, sometimes you want to keep the data private to have more control over it. If you have a variable that needs to be integer for example, and the value that was given to it is something else, then you will need to do a lot of checking and casting later on whenever you will need to use that. And if an object was passed instead of integer, it's even worse. You can't do anything.So? It's not your problem. Assuming someone else is using your code (if you can't remember which of your own variables it's safe to modify, you've deeper problems :)), if you note in the documentation or perhaps a comment that "this method must only ever be passed an integer" and some clever person decides to pass it an object, it's entirely their own fault. They will, in due course, notice and correct the problem. It's an issue with their code, not yours.
I think this is why, encapsulation was put in place for OOP paradigm.Encapsulation doesn't require private members. All that is necessary is to specify that certain interfaces will not change. I think you're misunderstanding the concept.
I am still a new learner about javascript clouser. Correct me if I'm wrong, but I think clouser should only be used in special cases and not all the time. If we have too much of clouser in a class, then it should probably be re-engineered. We also need to use it efficiently. And if we are not careful, ofcourse it's gonna have bad effect; just like anything else that exists. Things have both good effect and bad effect depending on how you use it.Closures are quite often the most efficient way of doing things, and there's certainly nothing wrong with using them. However, as with anything, their use has a performance cost; one which, in this case, happens to considerably outweigh the debatable benefits of using them for this purpose.

pman
05-28-2007, 12:04 AM
Hey Twey,

Thanks so much for this example



this.run = function()
{
setTimeout(
(function(me) {
return function() {
me.doSomething();
me = null; // prevent memory leaks in IE if the object holds a reference to a DOM node.
// Can get trickier if using recursive setTimeout()s or setInterval(), where the reference must be reused.
};
})(this),
500
);
}


It solved the problem. But the bolded "this" didn't work for me. So, in the constructor, I saved a reference to "this" and passed that reference instead of that bolded "this". So basically, it looked like this.


this.run = function()
{
setTimeout(
(function(me) {
return function() {
me.doSomething();
me = null; // prevent memory leaks in IE if the object holds a reference to a DOM node.
// Can get trickier if using recursive setTimeout()s or setInterval(), where the reference must be reused.
};
})(referenceToObj),
500
);
}


I wonder why the following didn't work


this.run = function()
{
setTimeout(function() {
referenceToObj.doSomething();
}, 500 );
}



if you note in the documentation or perhaps a comment that "this method must only ever be passed an integer" and some clever person decides to pass it an object, it's entirely their own fault. They will, in due course, notice and correct the problem. It's an issue with their code, not yours.

I had a feeling you would say this. lol....:D

Twey
05-28-2007, 12:12 AM
It solved the problem. But the bolded "this" didn't work for me. So, in the constructor, I saved a reference to "this" and passed that reference instead of that bolded "this". So basically, it looked like this."Didn't work" how? I don't see how that would fix the problem.
I wonder why the following didn't work

Code:
this.run = function()
{
setTimeout(function() {
referenceToObj.doSomething();
}, 500 );
}Again, I think there may be more going on here than you're mentioning. There's no reason the latter shouldn't work either.

pman
05-28-2007, 03:25 AM
"Didn't work" how? I don't see how that would fix the problem.Again, I think there may be more going on here than you're mentioning. There's no reason the latter shouldn't work either.

you might be right. I'm gonna go through the whole thing later when I find some time. Thanks by the way. You know a lot about advanced javascript. I'm sure I'll need your help again.

mwinter
05-28-2007, 08:22 PM
function aClassName(param1, param2)
{
//constructor begin

this.var1 = param1 ;
this.var2 = param2 ;

this.doSomething() ;

//constructor end

//privileged function

this.doSomething = function()
{
alert("I'm doing something...") ;
}
}


I don't think anyone has yet explained exactly why this fails to work. For a discussion of the various forms involving the function keyword, see my post in the Usenet thread, Function Declaration (http://groups.google.co.uk/group/comp.lang.javascript/msg/b694f3bc0e0c71c6). Feel free to ask any questions here - I'm not currently reading newsgroups.





function ClassAndConstructor(a, b, c) {
/* ... */

this.method1 = function(a) {
return a*this.prop3;
}
this.method2 = function(a) {
this.prop1 = a;
}

/* ... */
}


The two function objects defined above do not attempt to access "private" data: neither should be "privileged".



In ECMAScript there's another incentive to make everything public: if you use a closure to allow access to certain data only via privileged methods, that closure is recreated for each instance of the class instead of only once for the prototype, leading to a dramatic decrease in performance.

Whilst you are correct regarding the consequences of evaluating function expressions within constructor functions, I consider your statement to be misleading by using the word "dramatic". I would expect the word "slight" to be more accurate.



Since I'm not that experienced in OOP Javascript, I'm not sure about the difference between a privileged and public in javascript.

The word "privileged", when applied to methods, simply means that it can access "private" data. The following example is taken from a tutorial topic I wrote a while ago.



function MyObject() {
var privateMember = 5;

this.method1 = function() {return privateMember;};
}
MyObject.prototype.method2 = function() {return this.privateMember;};

In this example, method1 is said to be privileged as it, unlike method2 has access to privateMember. Because the function is created within the scope of the MyObject constructor function, all of the local variables and functions are accessible in its scope chain. Note that the return value of method2 would be undefined as there is no public member named privateMember.





this.run = function()
{
setTimeout(
(function(me) {
return function() {
me.doSomething();
me = null; // prevent memory leaks in IE if the object holds a reference to a DOM node.
// Can get trickier if using recursive setTimeout()s or setInterval(), where the reference must be reused.
};
})(this),
500
);
}


It seems to me that



this.run = function () {
var self = this;

setTimeout(function () {
self.doSomething();
}, 500);
};

would be just as effective and far simpler.