Eli Sand
Eli Sand

Reputation: 1032

Dynamic JavaScript object property value

I'm trying to figure out if there is a way to have an object property defined in its prototype as a dynamic value that can change every time an instance of the object is created. That's kind of the best way I can describe it; I have made a fiddle to show what I'm trying to do (though it doesn't work).

var Response = {
    LCID: 321
};


Date.prototype.LCID = Number(0);
Date.prototype.LCID.valueOf = function() { return Response.LCID; };


document.write((new Date()).LCID);
Response.LCID = 456;
document.write((new Date()).LCID);

http://jsfiddle.net/tx2fW/2/

What I'm trying to achieve is that Response.LCID can change over the lifetime of the code as you can see from me changing its value in the code later on. Whenever I create a Date object, I want the value of (new Date()).LCID to be the current value of Response.LCID, not just the value that it had when I first create Date.prototype.LCID.

Is there any way this can be done? The biggest limitation is that it has to work in JavaScript 1.5... though I would be curious to see if it could be done in recent versions.

Upvotes: 1

Views: 1081

Answers (3)

GillesC
GillesC

Reputation: 10874

Ok this does it using the __defineGetter__ method, however I am not sure how it will behave on all browser but that the only way I could find to achieve what you want (well I think this time it is what you want)

http://jsfiddle.net/tx2fW/6/ working example.

var Response = {
    LCID: 321
};

var d = Date.prototype;

d._LCID = Number(0);
d.getLCID = function() {
    if (d._LCID != Response.LCID) d._LCID = Response.LCID;
    return d._LCID ;
};
d.__defineGetter__("LCID", function() {
    return this.getLCID();
});

document.write((new Date()).LCID);
Response.LCID = 456;
document.write((new Date()).LCID);​

For alternative to __defineGetter__ please see this post

Upvotes: 1

Eli Sand
Eli Sand

Reputation: 1032

After some more research and experimenting, I was able to actually solve the issue. I had tried playing with the Date constructor and such, but I didn't have much luck in my initial trials - apparently because I overlooked the fact that the Date object is unique in that it differs in functionality depending on how it is called (as a function or object constructor). This means that you can't just do Date.prototype.constructor.apply(this, arguments) since all you'll ever get back is a string (the Date object is being called as a function).

After having found this thread and reading it, I came up with the following code that creates an actual Date object (or string if called as a function) and mimics the built-in Date object perfectly (as far as my tests show anyways). Every time a new Date object is created, it gets the LCID property which is dynamically generated during object creation which is exactly what I needed.

Date = (function(orig) {
    var date = function(a, b, c, d, e, f, g) {
        var object = (this instanceof Object ? (arguments.length < 1 ? new orig() : (arguments.length < 2 ? new orig(a) : (arguments.length < 4 ? new orig(a, b || 0, c || 1) : new orig(a, b, c, d || 0, e || 0, f || 0, g || 0)))) : orig());
        object.LCID = Response.LCID;

        return object;
    };
    date.prototype = orig.prototype;

    return date;
})(Date);

I also created a bunch of test cases to ensure that there is no difference from a built-in Date object, or using this code (comment out this code to see the results using the built-in Date object and compare).

var Response = { 'LCID': 123 };


Date = (function(orig) {
    var date = function(a, b, c, d, e, f, g) {
        var object = (this instanceof Object ? (arguments.length < 1 ? new orig() : (arguments.length < 2 ? new orig(a) : (arguments.length < 4 ? new orig(a, b || 0, c || 1) : new orig(a, b, c, d || 0, e || 0, f || 0, g || 0)))) : orig());
        object.LCID = Response.LCID;
        return object;
    };
    date.prototype = orig.prototype;
    return date;
})(Date);


var x = new Date();
document.writeln(x);
document.writeln(x.LCID);
document.writeln(x.getFullYear());
document.writeln(typeof x);
document.writeln(Object.prototype.toString.call(x));
document.writeln(x instanceof Date);
document.writeln("<br/>");

Response.LCID = 456;

var y = new Date();
document.writeln(y);
document.writeln(y.LCID);
document.writeln(y.getFullYear());
document.writeln(typeof y);
document.writeln(Object.prototype.toString.call(y));
document.writeln(y instanceof Date);
document.writeln("<br/>");

document.writeln(Date());
document.writeln(new Date());
document.writeln(new Date(2012));
document.writeln(new Date(2012, 7));
document.writeln(new Date(2012, 7, 14));
document.writeln(new Date(2012, 7, 14, 9));
document.writeln(new Date(2012, 7, 14, 9, 45));
document.writeln(new Date(2012, 7, 14, 9, 45, 27));
document.writeln(new Date(2012, 7, 14, 9, 45, 27, 687));

This is also available as an updated fiddle: http://jsfiddle.net/tx2fW/9/

Upvotes: 0

Rob W
Rob W

Reputation: 349012

Number(0) === 0. Modifying .valueOf and .toString on primitive values have no effect.

The correct way to do this is to pass an instance of the Number constructor, using the new operator:

var Response = {
    LCID: 321
};
Date.prototype.LCID = new Number();   // <-- Use the "new" operator
Date.prototype.LCID.valueOf = function() { return Response.LCID; };

Annotated demo and notes: http://jsfiddle.net/tx2fW/7/

  • LCID is an object. typeof new Date().LCID === 'object'.
  • LCID is a true instance of Number. new Date().LCID instanceof Number === true
  • LCID is equal to 321: (new Date().LCID == 321) === true
  • LCID is not identical to 321: (new Date().LCID === 321) === false.
    (because LCID is an object, and 321 is a primitive number value).

PS. If you're not familiar with == versus ===, see Which equals operator (== vs ===) should be used in JavaScript comparisons?

Upvotes: 1

Related Questions