Just want to know if this is safe way to clone arrays and plain objects avoiding infinite loops trigger by circular refrences:
//
!(( function ( methodName, dfn ) {
// add global 'clone' method identifier
this[methodName] = dfn();
} ).call(
self,
"clone",
function () {
var _api = {
// two arrays to keep track of what was cloned
originalValues : [],
clonedValues : [],
clone : function ( input ) {
throw new Error(
"Failed to clone value: " + input + "."
);
},
ownEach : function ( obj, func ) {
for ( var p in obj ) {
obj.hasOwnProperty( p )
&& func.call( obj, p, obj[p] );
}
return obj;
}
};
// helper functions
function corstr( o ) {
return Object.prototype.toString.call( o );
}
function corslice( args, i1, i2 ) {
return Array.prototype.slice.call( args, i1, i2 );
}
function isArray( o ) {
return corstr( o ) === "[object Array]";
}
function isObject( o ) {
return corstr( o ) === "[object Object]";
}
function bind( obj, fn ) {
return function () {
return fn.apply( obj, corslice( arguments ) );
};
}
function override( obj, method_v1, method_v2 ) {
var coreMethod = obj[method_v1];
return obj[method_v1] = function () {
return method_v2.apply(
this,
[ bind( this, coreMethod ) ]
.concat( corslice( arguments ) )
);
};
}
//
// build up clone method
// handles arrays and objects
override(
_api,
"clone",
function ( oldMethod, input ) {
var cloned, isarray = false;
if (
isArray( input )
&& ( isarray = true, cloned = [] )
|| isObject( input )
&& ( cloned = {} )
) {
_api.originalValues.push( input );
_api.clonedValues.push( cloned );
isarray
&& (
input
.forEach(
function ( val ) {
cloned.push( _api.clone( val ) );
}
),
true
)
|| (
_api.ownEach(
input,
function ( prop, val ) {
cloned[prop] = _api.clone( val );
}
)
);
return cloned;
} else {
return oldMethod( input );
}
}
);
// copies anything else
override(
_api,
"clone",
function ( oldMethod, input ) {
// here checks if value has been already cloned
// and returns it if it has
var k;
if ( ( k = _api.originalValues.indexOf( input ) ) != -1 ) {
return _api.clonedValues[k];
}
if (
!isArray( input )
&& !isObject( input )
) {
_api.originalValues.push( input );
_api.clonedValues.push( input );
return input;
} else {
return oldMethod( input );
}
}
);
return function ( value ) {
var out = _api.clone( value );
// empties arrays after _api.clone() func is done
_api.originalValues.length
= _api.clonedValues.length
= 0;
return out;
};
}
));
// test
var o = [];
var clon;
o.push( o );
o.push( { p : o } );
clon = clone( o );
console.log( o );
console.log( clon );
console.log( 'o === clon -> ' , o === clon );
console.log( 'o[0] === clon[0] -> ' , o[0] === clon[0] );
console.log( 'o[1] === clon[1] -> ' , o[1] === clon[1] );
console.log( 'o[1].p === clon[1].p -> ', o[1].p === clon[1].p );
//
// [[...], Object { p=[2]}]
// [[...], Object { p=[2]}]
// o === clon -> false
// o[0] === clon[0] -> false
// o[1] === clon[1] -> false
// o[1].p === clon[1].p -> false
//