function stringifyCyclicJSON(sourceObject, maxDepth = 5) { var knownObjects = {}; var jsonString = ''; recStringifyObject(sourceObject, '', 1, 'root'); return jsonString; function recStringifyObject(currentObject, indent, level, path) { var isArray = (currentObject instanceof Array); var subIndent = indent + '\t'; var i = 0; jsonString += (isArray ? '[' : '{'); if (isArray) { for (; i < currentObject.length; i++) { doSingleStringify(i, ''); } } else { for (var key in currentObject) { if (currentObject[key] !== undefined) { doSingleStringify(key, '"' + key + '": '); i++; } } } jsonString += (i > 0 ? '\n' + indent : ' ') + (isArray ? ']' : '}'); function doSingleStringify(key, addBefore) { var value = currentObject[key]; jsonString += '\n' + subIndent + (i > 0 ? ', ' : '') + addBefore; if (typeof value == 'object') { // first search in known Objects var found = false; for (var pathKey in knownObjects) { if (knownObjects[pathKey] == value) { jsonString += '"[' + pathKey + ']"'; found = true; } } if (!found) { var newPath = path + (isArray ? '[' + key + ']' : '.' + key); knownObjects[newPath] = value; if (level < maxDepth) { recStringifyObject(value, subIndent, level + 1, newPath); } else { jsonString += '"[' + (value instanceof Array ? 'Array' : 'Object') + ']"'; } } } else if (typeof value == 'function') { jsonString += '"[Function]"'; } else { jsonString += JSON.stringify(value); } } } }