object methods act like properties

N

Nathan White

I was curious if I can make an object method look and act like an
object property.

example:

function Circle(x,y,r){ this.x = x; this.y = y; this.r = r; }
Circle.prototype.area = function(){ return Math.PI * this.r * this.r; }

var c = new Circle(0,0,1);

I would like to get the area by calling it: var area = c.area;

instead of c.area();
 
B

Baconbutty

Generally I don't think so.

Mozilla is an exception, with the prototype.__defineGetter__ and
prototype.__defineSetter__ methods, which will do just what you want.

I don't know what other browsers support this.
 
M

Martin Honnen

Nathan said:
I was curious if I can make an object method look and act like an
object property.

example:

function Circle(x,y,r){ this.x = x; this.y = y; this.r = r; }
Circle.prototype.area = function(){ return Math.PI * this.r * this.r; }

var c = new Circle(0,0,1);

I would like to get the area by calling it: var area = c.area;

instead of c.area();

With Mozilla's JavaScript you can create getters (and setters of course
but not needed for your example):

function Circle(x,y,r){ this.x = x; this.y = y; this.r = r; }
Circle.prototype.__defineGetter__('area', function(){ return Math.PI *
this.r * this.r; });

var c = new Circle(0,0,1);
alert(c.area);

But that is not portable so not useful for scripting on the web in
general. But Mozilla's innerHTML implementation is nothing but a
getter/setter defined appropriately.
 
N

Nathan White

Actually I found a cross browser way of pulling this off using
closures:

function Circle(x,y,r){
var self = this;
this.x = x;
this.y = y;
this.r = r;

this.area = (function(){ return Math.PI * self.r * self.r; })();
}

the only thing is it seems to get all messed up when I use the
prototype chain, for example:

Circle.prototype.area = (function(){ return Math.PI * self.r * self.r;
})();

any idea how to use a closure in a prototype chain to accomplish the
result above?
 
M

Martin Honnen

Nathan said:
Actually I found a cross browser way of pulling this off using
closures:

function Circle(x,y,r){
var self = this;
this.x = x;
this.y = y;
this.r = r;

this.area = (function(){ return Math.PI * self.r * self.r; })();
}

But that computes area once in the constructor but not again if the r
property changes:

function Circle(x,y,r){
var self = this;
this.x = x;
this.y = y;
this.r = r;

this.area = (function(){ return Math.PI * self.r * self.r; })();
}

var circle = new Circle(0, 0, 1);
alert(circle.area);
circle.r = 200;
alert(circle.area);

That does not make much sense to me, you could as well use
this.area = Math.PI * this.r * this.r;
then.
 
V

VK

I was curious if I can make an object method look and act like an
object property.

<script type="text/javascript">

//If it's a real borning desire(?) you can use watch method (Mozilla,
NNx):


function Circle(x,y,r){
this.shape = 'circle';
this.x = x;
this.y = y;
this.watch('r',calcArea);
this.r = r;
calcArea(null,null,r);
function calcArea(arg1,arg2,arg3) {
this.area = Math.PI*Math.pow(arg3,2);
}
}

var c = new Circle(0,0,1);
alert(c.area);
c.r = 4;
alert(c.area);
</script>

// IE doesn't have watch / unwatch methods, you need to play with
ptopertyChanged and inheritance.
// But what do you have against ( ) ?
 
N

Nathan White

Martin,

I agree the example doesn't make much sense, I apologize.

Ideally, I want to have a static property but it won't be in the object
by default. I want a prototype based method that is called and
calculates a property and assigns the value to the same name as the
prototype method. Thus once the prototype method has been called once,
it has been shadowed and effectively no longer exists.

What I don't understand about the prototype chain is how do you go in
the opposite direction? Is this possible to go back down and find the
original instance that triggered the prototype chain?
 
M

Martin Honnen

VK wrote:

function Circle(x,y,r){
this.shape = 'circle';
this.x = x;
this.y = y;
this.watch('r',calcArea);
this.r = r;
calcArea(null,null,r);
function calcArea(arg1,arg2,arg3) {
this.area = Math.PI*Math.pow(arg3,2);

If you want to have this.r a value after the watch handler has been
called you should return
return arg3;
from the calcArea function. Otherwise you compute this.area correctly
but then set this.r to undefined.
 
V

VK

Ideally, I want to have a static property but it won't be in the object
by default. I want a prototype based method that is called and
calculates a property and assigns the value to the same name as the
prototype method. Thus once the prototype method has been called once,
it has been shadowed and effectively no longer exists.

This way you may want to play with polymorphic functions and
inheritance:


<script type="text/javascript">

function ClosedShape() {
// This we would call super-class in other language
switch(this.shape) {
case 'Circle':
this.area = Math.PI*Math.pow(this.r,2);
break;
default:
/*NOP*/
}
}

function Circle(x,y,r){
// This we would call inherited class in other language
this.shape = 'Circle'
this.x = x;
this.y = y;
this.r = r;
ClosedShape.call(this);
}

var c = new Circle(0,0,4);

alert(c.area);
</script>
 
V

VK

Martin said:
VK wrote:



If you want to have this.r a value after the watch handler has been
called you should return
return arg3;
from the calcArea function. Otherwise you compute this.area correctly
but then set this.r to undefined.

My fault!
 
M

Matt Kruse

Nathan said:
function Circle(x,y,r){ this.x = x; this.y = y; this.r = r; }
Circle.prototype.area = function(){ return Math.PI * this.r * this.r;
}
var c = new Circle(0,0,1);
I would like to get the area by calling it: var area = c.area;

One hack way of doing it is to make your properties private and only setable
from a setter method. Then, in that method, re-compute the area property.

But I disagree with the requirement to begin with. Area shouldn't be a
property. It's a computed value, so it shouldn't look like a property. I
know, semantics, but programmers get picky :)
 
R

rh

Nathan said:
Martin,

I agree the example doesn't make much sense, I apologize.

Ideally, I want to have a static property but it won't be in the object
by default. I want a prototype based method that is called and
calculates a property and assigns the value to the same name as the
prototype method. Thus once the prototype method has been called once,
it has been shadowed and effectively no longer exists.

Presumably you're trying to avoid repetition of the calcuating function
within each instance. Although it's not clear why you wish to use the
same name for the function and the static property, why wouldn't you
simply add

this.area = this.area();

to the Circle constructor function?
What I don't understand about the prototype chain is how do you go in
the opposite direction? Is this possible to go back down and find the
original instance that triggered the prototype chain?

That would possibly be

this.constructor.prototype

But natural inheritance and property override should give you what you
apparently want, as given above.

../rh
 
R

rh

Matt said:
One hack way of doing it is to make your properties private and only setable
from a setter method. Then, in that method, re-compute the area property.

But the OP indicates the area property will be static. Therefore, once
it has been computed and initialized, it doesn't get re-computed.
But I disagree with the requirement to begin with. Area shouldn't be a
property. It's a computed value, so it shouldn't look like a property.

It's not clear what your concept of a property is, but the ECMA
standard says:

"Properties are containers that hold other objects, primitive
values, or methods."

That evokes the question, if "it shouldn't look like a property", what
should it look like?

I gather what you're saying is that if an object property's content is
variable, that you would prefer to see a method call to retrieve the
value. But that's really an issue that relates to how the integrity of
an object is maintained, not whether the value is retrieved through a
method call.

For example, if the radius "r" of an instance of Circle was allowed to
be variable, the change to "r" could be done through a call to a
method. That call should result in the "area" property being updated
accordingly. There is no reason that the "area" property must be
retrieved as something other than a primitive, even if it is subject to
change.
I
know, semantics, but programmers get picky :)

Ah, but they do .... ;-)

../rh
 
Z

zwetan

I was curious if I can make an object method look and act like an
object property.

example:

function Circle(x,y,r){ this.x = x; this.y = y; this.r = r; }
Circle.prototype.area = function(){ return Math.PI * this.r * this.r; }

var c = new Circle(0,0,1);

I would like to get the area by calling it: var area = c.area;

instead of c.area();

well you can emulate a getter property if you modify the function toString
return
(works with MS JScript and SpiderMonkey JavaScript)

Object.prototype.addProperty = function( /*String*/ name, /*Function*/
getter, /*Function*/ setter )
{
var self = this;

if( setter == null )
{
setter = function() {};
}

this[name] = function()
{

if( arguments.length == 0 )
{
return getter.call( self );
}
else
{
setter.apply( self, arguments );
}

}

this[name].toString = function()
{
return getter.call( self );
}

}


//usage (note: the trace function is whatever document.write/printf/etc. you
want)

toto = {};

toto.getA = function()
{
return this._a;
}

toto.setA = function( value )
{
this._a = value;
}

toto.addProperty( "a", toto.getA, toto.setA );

trace( toto.a() ); //undefined
trace( toto.a ); //undefined

toto.a( "hello world" );

trace( toto.a() ); //"hello world"
trace( toto.a ); //"hello world"



//other usage

titi = function()
{

this._a = 1234;
this._b = null;

var _c = "secret";

this.getC = function()
{
return _c;
}

this.addProperty( "a", this.getA, null );
this.addProperty( "b", this.getB, this.setB );
this.addProperty( "c", this.getC, null );

}

titi.prototype.getA = function()
{
return this._a;
}

titi.prototype.getB = function()
{
return this._b;
}

titi.prototype.setB = function( value )
{
this._b = value;
}


foobar = new titi();

trace( foobar.c ); //"secret"
foobar.c( 567989 );
trace( foobar.c ); //"secret"

--------------------------

for your example:

Circle = function( /*Int*/ x, /*Int*/ y, /*Int*/ r )
{
this.x = x;
this.y = y;
this.r = r;

this.addProperty( "area", this.getArea, null );
}

Circle.prototype.getArea = function()
{
return Math.PI * this.r * this.r;
}

var c = new Circle(0,0,1);

trace( c.area ); //3.14159265358979

c.r = 50;

trace( c.area ); //7853.98163397448

I would like to get the area by calling it: var area = c.area;

done ;)

zwetan
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
474,431
Messages
2,571,679
Members
48,796
Latest member
Greg L.

Latest Threads

Top