Skip to content

Instantly share code, notes, and snippets.

@NV
Created November 26, 2011 18:22
Show Gist options
  • Save NV/1396086 to your computer and use it in GitHub Desktop.
Save NV/1396086 to your computer and use it in GitHub Desktop.
Clone an Object with all its circular references. Take that jQuery.extend!
function cloneObject(object) {
return extendObject({}, object);
}
function extendObject(base, object) {
var visited = [object];
// http://en.wikipedia.org/wiki/Adjacency_list_model
var set = [{value: base}];
_extend(base, object);
return base;
function _extend(base, object) {
for (var key in object) {
var value = object[key];
if (typeof value === 'object') {
var index = visited.indexOf(value);
if (index === -1) {
visited.push(value);
var newBase = base[key] = {};
set.push({up: base, value: newBase});
_extend(newBase, value);
} else {
base[key] = set[index].value;
}
} else {
base[key] = value;
}
}
}
}
// Example #1
var a = {};
a.self = a;
console.log(a, cloneObject(a));
// Example #2
var b = {
x: 1,
y: {
y1: 2,
z1: {
z2: 3
}
}
};
b.top = b;
b.y.top = b;
b.y.z1.top = b;
b.y.up = b;
b.y.z1.up = b.y;
b.y.z1.self = b.y.z1;
var bClone = cloneObject(b);
bClone.x = 42;
bClone.y.y1 = 13;
console.log(b, bClone);
@dr-skot
Copy link

dr-skot commented Jan 6, 2014

This is the right idea, but only works with object trees that contain no nulls, arrays, dates, or regexps.
http://jsfiddle.net/drskot/euU9j/

It could be made real-world ready with a little work, or you can use _.cloneDeep.
http://lodash.com/

@gjuchault
Copy link

const extendObject = (base, object) => {
    let visited = [object];

    // http://en.wikipedia.org/wiki/Adjacency_list_model
    let set = [{
        value: base
    }];

    if (Array.isArray(object)) {
        base = [];
    }

    const _extend = (base, object) => {
        for (let key in object) {
            if (object.hasOwnProperty(key)) {
                let value = object[key];
                console.log('clone', value);

                if (typeof value === 'object') {
                    let index = visited.indexOf(value);

                    if (index === -1) {
                        visited.push(value);

                        if (Array.isArray(value)) {
                            let newBase = base[key] = [];
                            set.push({
                                up   : base,
                                value: newBase
                            });
                            _extend(newBase, value);
                        } else {
                            let newBase = base[key] = {};
                            set.push({
                                up   : base,
                                value: newBase
                            });
                            _extend(newBase, value);
                        }
                    } else {
                        base[key] = set[index].value;
                    }
                } else {
                    if (Array.isArray(base)) {
                        base.push(value);
                    } else {
                        base[key] = value;
                    }
                }
            }
        }
    };

    _extend(base, object);

    return base;
};

const cloneObject = object => (Array.isArray(object)) ? extendObject([], object) : extendObject({}, object);

In ES6, with array support

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