Skip to content

Instantly share code, notes, and snippets.

@getify
Created August 16, 2012 21:25
Show Gist options
  • Save getify/3373779 to your computer and use it in GitHub Desktop.
Save getify/3373779 to your computer and use it in GitHub Desktop.
object JSON serialization that's circular-ref safe
// all this `toJSON()` does is filter out any circular refs. all other values/refs,
// it passes through untouched, so it should be totally safe. see the test examples.
// only extend the prototype if `toJSON` isn't yet defined
if (!Object.prototype.toJSON) {
Object.prototype.toJSON = function() {
function findCircularRef(obj) {
for (var i=0; i<refs.length; i++) {
if (refs[i] === obj) return true;
}
return false;
}
function traverse(obj) {
function element(el) {
if (typeof el === "object") {
if (el !== null) {
if (el instanceof Date || el instanceof Number || el instanceof Boolean || el instanceof String || el instanceof RegExp) {
return el;
}
else if (!findCircularRef(el)) {
return traverse(el);
}
}
return null;
}
return el;
}
var idx, tmp, tmp2;
if (Object.prototype.toString.call(obj) === "[object Array]") {
refs.push(obj);
tmp = [];
for (idx=0; idx<obj.length; idx++) {
tmp.push(element(obj[idx]));
}
refs.pop();
return tmp;
}
else if (typeof obj === "object") {
if (obj !== null) {
if (obj instanceof Date || obj instanceof Number || obj instanceof Boolean || obj instanceof String || obj instanceof RegExp) {
return obj;
}
else if (!findCircularRef(obj)) {
refs.push(obj);
tmp = {};
for (idx in obj) { if (obj.hasOwnProperty(idx)) {
tmp2 = element(obj[idx]);
if (tmp2 !== null) tmp[idx] = tmp2;
}}
refs.pop();
return tmp;
}
}
return null;
}
else return obj;
}
var refs = [], ret;
ret = traverse(this);
refs = [];
return ret;
};
// ES5-only: prevent this `toJSON()` from showing up in for-in loops
if (Object.defineProperty) {
Object.defineProperty(Object.prototype,"toJSON",{enumerable:false});
}
}
var a = {
b: 12,
c: true,
d: "foobar",
e: {
f: function() { alert("blah"); }, // functions get ignored
g: new Date(), // dates get their own `toJSON()` serialization called
h: [ true,1.3,"haha" ]
},
k: {},
l: /foobar/g // regexes get turned into an empty {}
};
a.i = a; // circular ref!!
a.e.i = a; // circular ref!!
a.e.j = a.e; // circular ref!!
a.e.h.push(a.e); // circular ref!! since it's in array, will be replaced with `null`
a.k.m = a.e; // **NOT** a circular ref, just a dupe ref, so leave it alone!!
// Look Ma! No circular refs!
JSON.stringify(a); // {"b":12,"c":true,"d":"foobar","e":{"g":"2012-08-17T12:11:05.647Z","h":[true,1.3,"haha",null]},"k":{"m":{"g":"2012-08-17T12:11:05.647Z","h":[true,1.3,"haha",null]}},"l":{}}
@getify
Copy link
Author

getify commented Aug 18, 2012

@mohsen1 -- in theory, you could certainly do JSON.stringify(window), but I just tried it and it gave me a stack overflow error (because my approach uses recursion). The same would probably be true of a DOM object (if it has a bunch of children, especially).

But, moreover, even if it DID work, there's a bunch of stuff you would lose in the transmission. All the functions built onto the window object for instance would not be transferred. Variables, properties, and other data would make the trip, yes, but what you'd get on the other side is only a shadow of the original window... definitely not a copy of it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment