Recover methods from saved Javascript obect

Advertisements

If I have a class declaration in Javascript with a simple method as so:

class Foo {
    constructor(a, b) {
        this.a = a;
        this.b = b;
    }
    sum() {
        return this.a + this.b;
    }
}

I can then create an object and call its sum() method.

var bar = new Foo(2, 5); // {"a": 2, "b": 5}
var total = bar.sum(); // 7

I can also store this object (bar) in a database or local storage by first converting it to a string:

localStorage.savedBar = JSON.stringify(bar); // "{\"a\": 2, \"b\": 5}"

However, when I retrieve it, I can no longer access its methods.

var retrievedBar = JSON.parse(localStorage.savedBar); // {"a": 2, "b": 5}
var secondTotal = retrievedBar.sum(); // retrievedBar.sum is not a function!

This is really annoying, as it means that I would have to reconstruct any class objects. I have to say,

var goodRetrievedBar = new Foo(retrievedBar.a, retrievedBar.b);
var goodSecondTotal = goodRetrievedBar.sum(); // 7

which might seem fine for this scenario, but as soon as I even think about more deeply nested data structures, I very quickly enter the land of for loops and complex logic for a seemingly simple task. Also, if I wanted to make changes to my class in the future:

class Foo {
    constructor(a, b, c) {
        this.a = a;
        this.b = b;
        if (typeof c !== 'undefined') {
            this.c = c;
        }
    }
    sum() {
        if (this.c) {
            return this.a + this.b + this.c;
        }
        return this.a + this.b;
    }
}

I would also have to update my parsing code to support the additional property, c, when the changes I made to the class wouldn’t require me to make any other changes in my code.

Is there an easier way to force an object to assimilate itself into a class somehow? What is the standard way to deal with this in a simple and effective way?

>Solution :

Functions generally can’t be serialized and deserialized in all cases (due to closures), nor can objects with internal prototypes (other than Object.prototype) have those prototypes reflected after deserialization.

What you can do is serialize the own-properties of the instance, then when deserializing, use Object.create so that the internal prototype of the new instance is the class prototype.

class Foo {
    constructor(a, b, c) {
        this.a = a;
        this.b = b;
        if (typeof c !== 'undefined') {
            this.c = c;
        }
    }
    sum() {
        if (this.c) {
            return this.a + this.b + this.c;
        }
        return this.a + this.b;
    }
}
const f = new Foo(1, 2, 3);
const stringified = JSON.stringify(f);

const retrievedInstance = Object.create(Foo.prototype);
Object.assign(retrievedInstance, JSON.parse(stringified));
console.log(retrievedInstance.sum());

Note that this approach won’t work if you have anything stored in closures somewhere in the original object rather than in properties on the instance.

Leave a ReplyCancel reply