Say you have a Person type:
function Person(name){this.name = name
}
Person.prototype.sayHi = function(){return "Hi! I am " + this.name
}
You create an instance called tommy:
> tommy = new Person('Tommy')> tommy.sayHi()
Hi! I am Tommy
With a little magic from a toJson function that I whipped up:
> tommyJson = toJson(tommy)
> tommyJson
{__proto__: Person.prototype, name: "Tommy"}tommyJson is now a string representation of tommy. You can see that it sets the special __proto__ link to Person.prototype, which will do its magic when it's deserialized. Now let's deserialize it back:
> tommyCopy = fromJson(tommyJson)
> tommyCopy.sayHi()
Hi! I am Tommy
> tommyCopy.constructor
function Person(name){ this.name = name;
}
> tommyCopy === tommy
false
As you can see, the deserialized object tommyCopy is not just a plain object literal, but has all of the behaviors of Person.
The code for toJson and fromJson is so small I don't even bother putting it on github, so I'll just post it here(You may need ecma5array if you want to run it on a sucky browser):
function toJson(obj){
function keys(o){
var ret = []
for (var key in o){
var val = o[key]
if (o.hasOwnProperty(key)) // exclude attributes in the parent
ret.push(key)
}
return ret
}
function quote(str){
return '"' + str + '"'
}
if (obj === null || obj === undefined)
return String(obj)
else if (obj.constructor === String)
return quote(obj)
else if (obj.constructor === Array)
return '[' + obj.map(toJson).join(', ') + ']'
else if (obj.constructor === Object)
return '{' + keys(obj).map(function(key){return key + ': ' + toJson(obj[key])}).join(', ') + '}'
else if (obj.constructor && obj.constructor.name)
return '{' + ['__proto__: ' + obj.constructor.name + '.prototype'].concat(
keys(obj).map(function(key){return key + ': ' + toJson(obj[key])})).join(', ') + '}'
else
return String(obj)
}
function fromJson(str){
return eval('(' + str + ')')
}