views:

721

answers:

3

Help, I have this class

var jMath = {

    pi2: Math.PI,

    foo: function() {
        return this.pi2;
    }
}

I want to make the pi2 constant and i want jMath to inherit from Math object. How do I do that?

+3  A: 

Oh amusing, scratch all that, this is the correct version:

function JMath() {
   this.foo = function() {
        return this.PI;
    }
}

JMath.prototype = Math;

var jMath = new JMath();
alert(jMath.foo());

(which matches what the other answer is here)

(I originally tried to set the prototype using "JMath.prototype = new Math()" which is how I've seen it other places, but the above works)

Edit

Here's one way to do it as a singleton

//  Execute an inline anon funtion to keep
//  symbols out of global scope
var jMath = (function()
{
    // Define the JMath "class"
    function JMath() {
     this.foo = function() {
      return this.PI;
     }
    }

    JMath.prototype = Math;

    // return singleton
    return new JMath();
})();

//  test it
alert( jMath.PI );

//  prove that JMath no longer exists
alert( JMath );
altCognito
That beats the whole purpose of inheritance. I dont want to redefine all the Math functions.
kunjaan
That works, it's shorter, but creates an independent foo() method for each object. If you are creating a lot of JMath objects then you'll want to create an independent prototype.
Jason S
The new code works. Thanks. How can I use it for a singleton class?var bar = { x : "value1"}can i make my bar also a Math prototype? I just need one jMath object that is instantiated automatically.
kunjaan
Thanks Peter. I was exhausted after fumbling around with that.
altCognito
Your code rocks. Thanks.
kunjaan
The reason why JMath.prototype = new Math() doesn't work is because Math is not a constructor. It's just an object. https://developer.mozilla.org/En/Core_JavaScript_1.5_Reference/Global_Objects/Math
Chetan Sastry
Don't use "window", it only applies to browsers; there are other places where Javascript runs. Use "this" in a global scope, instead.
Jason S
Here, I'll tweak it so it works more normally.
Jason S
+2  A: 

Consider using prototype:

function JMath() {};
JMath.prototype = {
    pi2: Math.PI,

    foo: function() {
        return this.pi2;
    }
}

var j = new JMath(); 
j.pi2=44; j.foo(); // returns 44
delete j.pi2; j.foo(); // now returns Math.PI

The difference between this and @altCognito's answer is that here the fields of the object are shared and all point to the same things. If you don't use prototypes, you create new and unlinked instances in the constructor. You can override the prototype's value on a per-instance basis, and if you override it and then decide you don't like the override value and want to restore the original, use delete to remove the override which merely "shadows" the prototype's value.

Edit: if you want to inherit all the methods and fields of the Math object itself, but override some things without affecting the Math object, do something like this (change the name "Constructor1" to your liking):

function Constructor1() {};
Constructor1.prototype = Math;
function JMath() {};
JMath.prototype = new Constructor1();
JMath.prototype.pi2 = JMath.prototype.PI;
JMath.prototype.foo = function() { return this.pi2; }

var j = new JMath();
j.cos(j.foo()); // returns -1

edit 3: explanation for the Constructor1 function: This creates the following prototype chain:

j -> JMath.prototype -> Math

j is an instance of JMath. JMath's prototype is an instance of Constructor1. Constructor1's prototype is Math. JMath.prototype is where the overridden stuff "lives". If you're only implementing a few instances of JMath, you could make the overridden stuff be instance variables that are setup by the constructor JMath, and point directly to Math, like @altCognito's answer does. (j is an instance of JMath and JMath's prototype is Math)

There are 2 downsides of augmenting-an-object-in-the-constructor. (Not actually downsides necessarily) One is that declaring instance fields/methods in the constructor creates separate values for each instance. If you create a lot of instances of JMath, each instance's JMath.foo function will be a separate object taking up additional memory. If the JMath.foo function comes from its prototype, then all the instances share one object.

In addition, you can change JMath.prototype.foo after the fact and the instances will update accordingly. If you make the foo function in the constructor as a per-instance method, then once JMath objects are created, they are independent and the only way to change the foo function is by changing each one.


edit 2: as far as read-only properties go, you can't really implement them from within Javascript itself, you need to muck around under the surface. However you can declare so-called "getters" which effectively act as constants:

JMath.prototype.__defineGetter__("pi2", function() { return Math.PI; });
JMath.prototype.__defineSetter__("pi2", function(){}); // NOP
var j = new JMath();
j.pi2 = 77; // gee, that's nice 
// (if the setter is not defined, you'll get an exception)
j.pi2; // evaluates as Math.PI by calling the getter function

Warning: The syntax for defining getters/setters apparently is not something that IE doesn't implement nicely.

Jason S
Can I make my jMath inherit all the methods from Math object? And your code still allows the user to mutate pi2.
kunjaan
The edit I posted does that. In general it is very difficult to stop a determined user from mutating methods/fields. You can use a "private" value created in a closure, but you need to state what you want before we can tell you how to do it.
Jason S
Read only fields like Math.PI are not possible to define within Javascript (ECMAscript) itself; they are possible to set up within the implementing environment, however.
Jason S
Inheritance worked! thanks. is there a way I can make my above JSON class inherit the prototype. And why do we need to make a separate constructor?
kunjaan
You don't *need* the separate constructor, especially if you're only making one instance of it, use altCognito's method. Be sure to try to understand how this works!
Jason S
+1  A: 

User-defined object properties can't be constant. Math (and a few other objects) is a special built-in - it has read-only properties and functions. But it's not a constructor - it's just a static object (Math.constructor === Object).

And because JavaScript has prototypal inheritance, and not classical, you can't inherit from Math. (Read more here)

What you can do, however, is define a prototype. When a property isn't found locally, the JS parser looks for that property on the current object's prototype. altCognito's current solutions shows this very well.

I'm curious about exactly what it is you're trying to achieve. Perhaps something like this is what you want?

var jMath = function()
{
    const pi2 = Math.PI;

    this.getPi2 = function()
    {
        return pi2;
    }
}

var j = new jMath;
alert( j.getPi2() );
Peter Bailey
i read that const does not have cross-browser capability.
kunjaan
That's true - I forgot about that. JScript doesn't support const. The best you're going to get is to use a method w/a return value, and skip the idea of using a property altogether.
Peter Bailey