From 6a6cb49f2da571470a6b024b1e8db5ef2080b946 Mon Sep 17 00:00:00 2001 From: James Halliday Date: Fri, 18 Feb 2011 05:45:05 -0800 Subject: [PATCH] simpler clone implementation --- index.js | 43 ++++++++++++++++++------------------------- test/circular.js | 16 ++++++++++++++++ 2 files changed, 34 insertions(+), 25 deletions(-) diff --git a/index.js b/index.js index c315d4d..6bc4d97 100644 --- a/index.js +++ b/index.js @@ -43,38 +43,31 @@ Traverse.prototype.nodes = function () { }; Traverse.prototype.clone = function () { - // clone refObj for a properly immutable interface: - var refs = []; - var nodes = []; + var parents = [], nodes = []; - return (function clone (ref) { - if (typeof ref == 'object' && ref !== null) { - var node = Array.isArray(ref) ? [] : {}; - refs.push(ref); - nodes.push(node); + return (function clone (src) { + for (var i = 0; i < parents.length; i++) { + if (parents[i] === src) { + return nodes[i]; + } + } + + if (typeof src === 'object' && src !== null) { + var dst = Array.isArray(src) ? [] : Object.create(src.__proto__); - Object.keys(ref).forEach(function (key) { - var i = refs.indexOf(ref[key]); - if (i >= 0) { - node[key] = nodes[i]; - } - else { - node[key] = clone(ref[key]); - } - + parents.push(src); + nodes.push(dst); + + Object.keys(src).forEach(function (key) { + dst[key] = clone(src[key]); }); - refs.pop(); + parents.pop(); nodes.pop(); - - // To make instanceof work: - if (!Array.isArray(ref)) node.__proto__ = ref.__proto__; - - // Probably there are other attributes worth copying - return node; + return dst; } else { - return ref; + return src; } })(this.value); }; diff --git a/test/circular.js b/test/circular.js index 1857c5a..c735d18 100644 --- a/test/circular.js +++ b/test/circular.js @@ -78,3 +78,19 @@ exports.circDubUpMap = function () { assert.eql(c, { x : [ 1, 2, 3, [ 4, 5, '...' ] ], y : [ 4, 5, '...' ] }); }; + +exports.circClone = function () { + var obj = { x : [ 1, 2, 3 ], y : [ 4, 5 ] }; + obj.y[2] = obj; + obj.x.push(obj.y); + + var clone = Traverse.clone(obj); + assert.ok(obj !== clone); + + assert.ok(clone.y[2] === clone); + assert.ok(clone.y[2] !== obj); + assert.ok(clone.x[3][2] === clone); + assert.ok(clone.x[3][2] !== obj); + assert.eql(clone.x.slice(0,3), [1,2,3]); + assert.eql(clone.y.slice(0,2), [4,5]); +};