1

Ok, I'm trying to track any changes made to a huge form on a web application. When the page is loaded, I create a JS object that 'captures' the initial state of all input fields (selects, radio buttons, checkboxes etc...).
When the user alters the value of any of the literally hundreds of input elements, the new value is tracked in a second object. When the user clicks Update, these two objects are compared and only those values that have been changed are sent, to update the data accordingly.

Rather then building 2 completely separate objects, I thought it wise to use inheritance:

var initialState = getInitialState();//eg:{bar:'1',foo:'bar',atom:'bomb',some:{nested:'objects'}}
var tracker = Object.create(initialState);

As things go, the tracker object might end up looking something like this:

{bar:'0',foo:'bar',atom:'peace',some:{nested:'objects'}}

When calling JSON.stringify on this object in FF and chrome, all is well: only the objects' own properties are returned. Not so in IE: the tracker has no prototype property, so it would appear that Object.create creates copies rather then inheritance chains?
tracker.__proto__ === initialState returns true, whereas tracker.prototype === initialState evaluates to false, in fact the tracker.prototype property is undefined.

Question1: is there an alternative way to set up an inheritance chain in IE that allows me to peel away the unchanged prototype values?

Question2:I'd also like a method -if at all possible- to set up an inheritance chain that allows for nested objects. As things are now, the nested objects are dealt with by iterating over the main object, using a recursive function. Kind of silly, since that's what I'm trying to omit.

In short:
I want to know if this is out there:

var a = {bar:'1',foo:'bar',atom:'bomb',some:{nested:'objects'}};
var b = Object.magicDeepCreate(a);//<=== preferably X-browser
b.bar = '0';
b.bar.some.nested = 'stuff';
console.log(JSON.stringify(b));
//{"bar":"0","some":{"nested":"stuff"}}

As always: no jQuery tag, means no jQuery
Note: by IE I mean that monstrosity IE8, not IE9 (company policy, sadly)

hippietrail
  • 15,848
  • 18
  • 99
  • 158
Elias Van Ootegem
  • 74,482
  • 9
  • 111
  • 149
  • 1
    The `prototype` property of an instantiated object is *always* undefined. `__proto__` is a [non-standard property which is depreciated in Firefox](https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Object/Proto). Why don't you simply bind an `change` event handler to the input fields and track changes that way? – Matt Jul 19 '12 at 10:56
  • That's how I track the changes, but instead of checking `tracker.hasProperty(...)` a hundreds of times (remember: 200+ inputs is no exception) I'd rather be able to just set the new value and stringify only those that have been altered – Elias Van Ootegem Jul 19 '12 at 11:29

1 Answers1

2

tracker.__proto__ === initialState returns true, whereas tracker.prototype === initialState evaluates to false, in fact the tracker.prototype property is undefined.

The __proto__ property is non-standard and FF-only. To get the prototype object of an object, use Object.getPrototypeOf(). The prototype property of function objects is a property referencing the object from which all instances of that function (created using new) inherit.

Not so in IE

Object.create() is not supported at all in IE8. Did you use the common shim or does it silently fail? Or did you even use a function that really copies all properties?

Object.magicDeepCreate(a), preferably X-browser

That should be simple, assuming that all target browsers implement Object.create:

Object.deepCreate = function deepCreate(o) {
    var res = Object.create(o);
    for (var i in o)
        if (Object.hasOwnProperty(o, i) && typeof o[i] == "object")
             res[i] = deepCreate(o[i]);
    return res;
};

stringify only those that have been altered

That should be standard behaviour of JSON.stringify - the prototype object is not taken into account.

However, I'm not sure why you need inheritance at all for that tracker object. Just use an empty object, and add all properties that have been altered. If you want to delete those that have been reset to initial state, you could store that in an extra object to compare with - but there is no reason for inheritance. Just use:

Object.emptyStructure = function s(o){
    var res = {};
    for (var i in o)
        if (typeof o[i] == "object")
            res[i] = s(o[i]);
    return res;
};
var initialState = getInitialState();
var tracker = Object.emptyStructure(initialState);

// set:
if (newVal == initialState.some.nested)
    delete tracker.some.nested;
else
    tracker.some.nested = newVal;
Bergi
  • 630,263
  • 148
  • 957
  • 1,375
  • Thanks, the `deepCreate` method you suggested is similar to the one I'm currently using, though and still requires a loop of sorts. The thing that bugs me at times is that the JSON string contains various voids `{}`. I wanted to use inheritance seeing as the original and tracker objects both follow the same structure, and are 4 levels deep. Not using inheritance, as I am currently doing, requires too much effort and checks to build IMO, and then some more to compare. I was hoping there would be a way to avoid all that manual labour. Oh, and I noticed DC's `Object.create()` is defined for IE8 – Elias Van Ootegem Jul 19 '12 at 12:11
  • Hm, those empty objects need be - they only say that no property of them has changed, but that they still exist. – Bergi Jul 19 '12 at 12:19
  • Who is DC? The question is not whether, but *how* Object.create is implemented in IE8 - it is not native. Could you just print out the function? – Bergi Jul 19 '12 at 12:20
  • Why Douglas Crockford, of course... Here's the code: `if (!Object.create || typeof Object.create !== 'function') { Object.create = function(o) { function F(){}; F.prototype = o; return new F(); } }` If you would like an idea of the scale of the objects I'm wresteling with, I can provide a small example here somewhere, too – Elias Van Ootegem Jul 19 '12 at 12:27
  • Ah, OK, then it should work. If your JSON.stringify takes prototypes into account, maybe that function is screwed up? – Bergi Jul 19 '12 at 12:39
  • That's just it: the function I've pasted here is exactly the same as I have in my script, and as far as `JSON.stringify` is concerned: I rely on the browsers implementation for all good, W3C compliant browsers, and include `json2.js` for the EvIl IE, which I got from json.org... I've left all native prototypes untouched, except for `String`, which I gave a `trim` method for IE, and a `ucFirst` for neigh on browsers. Those methods are all in _strict mode_, I've tested using `tracker.hasOwnProperty`, but even when all return false, the json string is identical to the one gotten from its 'parent' – Elias Van Ootegem Jul 19 '12 at 12:50
  • Well, the FAQ of this site states that the answer you get might not the one you were hoping for. I guess this is somewhat the case here: I was hoping for the perfect `deepCreate` method, and I now know it doesn't exist. I'll approve this as the right answer and stick to my guns for the time being. Thanks for the feedback – Elias Van Ootegem Jul 19 '12 at 12:57