Javascript Stringify Nested Objects with Loop References I created these functions that will take any object and stringify it, and can then de-stringify it later.

function stringify(objectToCopy, exceptionClasses = []) {

    // Listify.
    var objects = [];

    var que = [objectToCopy];
    while(que.length &gt; 0){
        var o = que.pop();
        objects.push(o);
        var propNames = Object.getOwnPropertyNames(o);
        for (let i = 0; i &lt; propNames.length; i++) {
            const n = propNames[i];
            var property = o[n];
            if (
                typeof property == "object" &amp;&amp; property != null &amp;&amp;
                objects.indexOf(property) == -1 &amp;&amp;
                que.indexOf(property) == -1 &amp;&amp;
                exceptionClasses.indexOf(property.constructor.name) == -1
            ) {
                que.push(property);
            }
        }
    }

    // Stringify.
    var stringifiedList = [];

    for (let i = 0; i &lt; objects.length; i++) {
        const o = objects[i];

        var temp = {
            "className": o.constructor.name,
            "properties": [],
        };

        var propNames = Object.getOwnPropertyNames(o);
        for (let i = 0; i &lt; propNames.length; i++) {
            const n = propNames[i];
            var property = o[n];
            if (typeof property == "object" &amp;&amp; property != null){
                if (exceptionClasses.indexOf(property.constructor.name) == -1) {
                    temp.properties.push({
                        "isObject": true,
                        "name": n,
                        "content": objects.indexOf(property), // content will here refer to the index of where the object is.
                    });
                }
            }else{
                var content = JSON.stringify(property);
                if (content == null) {
                    content = null; // Converts from undefined to null.
                }
                temp.properties.push({
                    "isObject": false,
                    "name": n,
                    "content": content,
                });
            }
        }

        stringifiedList.push(temp);
    }

    //
    return JSON.stringify(stringifiedList);
}
function deStringify (string) {
    var stringifiedList = JSON.parse(string);


    // Listify.
    var objects = [];
    for (let i = 0; i &lt; stringifiedList.length; i++) {
        const data = stringifiedList[i];

        var cleanName = /[a-zA-Z_$][0-9a-zA-Z_$]*/.exec(data.className).toString();

        if (cleanName != data.className) {
            throw "ERROR: Class name validation failed."
        }

        var newObject = Object.create(eval(cleanName).prototype);
        objects.push(newObject);
    }

    // Destringify
    for (let i = 0; i &lt; stringifiedList.length; i++) {
        const data = stringifiedList[i];
        const obj = objects[i];

        for (let j = 0; j &lt; data.properties.length; j++) {
            const property = data.properties[j];
            if (property.isObject){
                obj[property.name] = objects[property.content];
            }else{
                obj[property.name] = JSON.parse(property.content);
            }
        }
    }

    return objects[0];
}

// Testing.
var a = [6,5,4,3];
var b = [a, a, "alpha", "beta"];
b.push(b);
a.push(b);

var s = stringify(a);
console.log(deStringify(s));

/* Output:
(5) [6, 5, 4, 3, Array(5)]
    0:  6
    1:  5
    2:  4
    3:  3
    4:  Array(5)
        0:  (5) [6, 5, 4, 3, Array(5)]
        1:  (5) [6, 5, 4, 3, Array(5)]
        2:  "alpha"
        3:  "beta"
        4:  (5) [Array(5), Array(5), "alpha", "beta", Array(5)]
        length: 5
        __proto__:  Array(0)
    length: 5
    __proto__:  Array(0)
*/


The reason I don't only use JSON.stringify() is because it does not conserve the prototype, and also does not support loop references. Is this approach hacky? Is there something about the objects I duplicate that I am missing when I only duplicate its properties? I know this code doesn't work for DOM objects or objects related to webgl.
JSON.stringify() and JSON.parse() were meant to serialize and deserialize data. This is why it omits non-data (functions, regex, etc.) on purpose. When you mention serializing prototypes, you are serializing behavior and object relationships, not data. It's the same reason why JSON is preferred over XML for AJAX, because JSON is a data exchange format while XML is a document format.

I suggest you rethink your approach before this becomes a rabbit hole.

FWIW, JSON.serialize() accepts a replacer function as second arg, while JSON.parse() accepts a reviver function as second arg. If what you're building is some kind of object mapper, you can use the replacer to insert metadata about the constructor for that instance, and use the reviver to pick up that metadata and customize how that object instance is created.