1

This question is a follow-up to my last question: JavaScript Serialization and Methods. While related, I believe this is different which is why I started this thread. Regardless...

I have a complex tree of objects that I need to pass around between pages. Because of this, I'm attempting to serialize and deserialize my objects. One of my objects in particular has several collections of child objects. Each of those child object types has a function on it, that I'm trying to call. Unfortunately, I am not having any luck. I setup a test project in an attempt to isolate the problem and get to the bottom of it. Currently, I have my JavaScript Objects defined in a seperate file called objects.js. That file looks like this:

objects.js

function MyChild() { this.init(); }
MyChild.prototype = {
    data: {
        id: 0,
        fullName: "",
    },

    init: function () {

    },

    save: function (key) {

    },

    load: function (key) {

    },

    test: function () {
        alert("Testing Child functions");
    }
}

function MyParent() { this.init(); }
MyParent.prototype = {
    data: {
        id: "",
        children: null
    },

    init: function () {
        this.data.children = [];
    },

    save: function (key) {
        window.localStorage.setItem(key, JSON.stringify(this.data));
    },

    load: function (key) {
        var temp = window.localStorage.getItem(key);
        if (temp != null) {
            this.data = JSON.parse(temp);
            $.each(this.data.children, function (i, r) {
            });
        }
    },

    test: function () {
        alert("Testing Parent functions");
    }
}

I am creating, serializing, deserializing, and attempting to interact with these objects in an .html file. That .html file is shown here:

test.html

<div>
    <input id="button1" type="button" value="Create Object Tree" onclick="button1Click();" /><br />
    <input id="button2" type="button" value="Retrieve and Execute" onclick="button2Click();" /><br />
</div>

<script type="text/javascript">
    function button1Click() {
        var child = new MyChild();
        child.data.id = 1;

        var p = new MyParent();
        p.data.id = "A";
        p.data.children.push(child);
        p.save("A");
    }

    function button2Click() {
        var storedParent = new MyParent();
        storedParent.load("A");

        storedParent.test();                          // This works
        alert(storedParent.data.children.length);     // This displays "1" like I would expect
        alert(storedParent.data.children[0].data.id); // This does NOT work. 
        storedParent.data.children[0].test();         // This does NOT work.   
    }
</script>

I'm not sure what I'm doing wrong. Can someone please help me understand this? Can somone please help me fix my example. I have a hunch that I'm not serializing MyChild objects properly. But I don't understand how I should be serializing / deserializing them in relation to MyParent.

Thank you!

Community
  • 1
  • 1
user208662
  • 10,869
  • 26
  • 73
  • 86
  • did you really mean to put `data` in the prototype chain? That would make the data shared by every instance. – Alnitak Mar 12 '12 at 16:09
  • no. I'm don't fully know what I'm doing. I'm trying to understand this object-oriented JavaScript thing. I come from a C# background. I assumed this approach created instance-level variables. Am I wrong? How do I fix it? – user208662 Mar 12 '12 at 16:20
  • @user208662 what Alnitak mentions above is what was wrong with the accepted answer on your other post ;) – Dagg Nabbit Mar 12 '12 at 17:59

1 Answers1

2

You need to store your data within each object, not within its prototype.

Data stored in in the prototype of an object is shared between all instances and won't be serialised by JSON.stringify, so your object data never ends up in the local storage.

To fix, add data to this within the this.init() function:

MyChild.prototype = {
    init: function() {
        this.data = {
            id: 0,
            fullName: ""
        };
    },
    ...
}

MyParent.prototype = {
    init: function() {
        this.data = {
            id: "",
            children: []
        }
    },
    ...
}

Working sample at http://jsfiddle.net/alnitak/fdwVB/

Note that attaching the functions in MyChild to the retrieved data is tricky. The code below appears to work:

load: function(key) {
    var temp = window.localStorage.getItem(key);
    if (temp != null) {
        this.data = JSON.parse(temp);
        var children = this.data.children;
        for (var i = 0; i < children.length; ++i) {
            children[i] = $.extend(new MyChild(), children[i]);
        }
    }
},

But note that it works by constructing a new "default" child for each retrieved entry, and then overwriting that child's data with the serialised values.

This could be problematic if the constructor has "side effects".

A better approach might be to allow the MyChild() constructor to take an optional complete object of initial values to use instead of the default values.

Alnitak
  • 334,560
  • 70
  • 407
  • 495
  • Thank you for your response. However, this still won't let me call the "test" function on "MyChild. For instance, if I do "storedParent.data.children[0].test()" I get a TypeError. – user208662 Mar 12 '12 at 17:09
  • ah, yes, didn't notice that part. The issue you have there is that deserialising the data recreates the data, but doesn't attach the object to its prototype. I'll see if I can fix the fiddle to achieve that. – Alnitak Mar 12 '12 at 17:15
  • thank you for your persistence. for the life of me, I can't figure it out. – user208662 Mar 12 '12 at 20:14
  • I just noticed you solved it. Seriously, I cannot say thank you enough. – user208662 Mar 13 '12 at 14:26