Hi,

Object.clone is documented as being a shallow copy:
http://www.prototypejs.org/api/object/clone

"Deep" cloning is more complicated than it seems at first.  A naive
version would be along these lines:

Object.extend(Object, {
    deepClone: function(source) {
        var result;
        var property;
        var val;

        result = {};
        for (property in source)
        {
            val = source[property];
            if (typeof val == "object" && !Object.isFunction(val))
            {
                result[property] = Object.deepClone(val);
            }
            else
            {
                result[property] = val;
            }
        }
        return result;
    }
});

Caveat: I'm not at all sure "typeof val == 'object' && !
Object.isFunction(val)" is an adequate check for whether we should
clone the property.

BUT, note that that does NOT correctly handle the full object graph.
Consider:

    var a, thing;

    thing = { name: 'original' };
    a = {};
    a.first = thing;
    a.second = thing;

    a.first.name = 'changed';

At this point, a.first.name and a.second.name both have the value
'changed'.  But if you used the deepClone above before changing it:

    var a, thing;

    thing = { name: 'original' };
    a = {};
    a.first = thing;
    a.second = thing;

    a = Object.deepClone(a); // <= ADDED THIS

    a.first.name = 'changed';

...then a.first.name is 'changed' but a.second.name is 'original'.
The "deep" clone is not a correct clone, because it didn't recognise
that two properties referenced the same instance.

It gets even worse if the object refers to itself, as the deepClone
function above would infinitely recurse.

Handling the full graph correctly would require that during a
particular cloning operation, you maintained a bag of cloned objects
keyed by *identity* (===) and reused the clones if you'd already
cloned them in the past.  Something like this:

(On pastie: http://pastie.org/287641)
* * * * * *
/**
 * A very quick-and-dirty bag for having values associated with  * a
key that's compared by
 * identity (===)
 */
var SlowIdentityBag = Class.create({

    /**
     * Create an empty bag.
     */
    initialize: function() {
        this.items = [];
    },

    /**
     * Get an entry from the bag for the given key.
     *
     * @param   key     The key to search for
     * @return  the associated value, or undefined if not found
     */
    get: function(key) {
        var index;
        var entry;

        for (index = this.items.length - 1; index >= 0; --index)
        {
            entry = this.items[index];
            if (entry.key === key)
            {
                return entry.val;
            }
        }

        return undefined;
    },

    /**
     * Put the given value in the bag with the given key.
     *
     * @param   key     Take a wild guess
     * @param   val     See above
     */
    put: function(key, val) {

        this.items.push({ key: key, val: val});
    }
});

Object.extend(Object, {
    /**
     * Deeply clone an object, preserving the object graph by only
cloning each object once and
     * reusing it if necessary.
     *
     * @param   source              The source object to clone
     * @param   clonesByIdentity    Usually omitted; if given, a
SlowIdentityBag containing
     *                              previously-cloned objects to reuse
keyed by the originals.
     *                              MUST NOT contain 'source'.
     */
    deepClone: function(source, clonesByIdentity) {
        var result;
        var property;
        var val;

        // A blank result object
        result = {};

        // Usually we create this unless we're calling ourselves
        if (!clonesByIdentity)
        {
            clonesByIdentity = new SlowIdentityBag();
        }

        // The source may be self-referential, so remember the
instance
        // we're cloning it to at the outset
        clonesByIdentity.put(source, result);

        // Process the properties
        for (property in source)
        {
            val = source[property];
            if (typeof val == "object" && !Object.isFunction(val))
            {
                // Something we should clone, if we haven't already.
                // (That check is probably not adequate.)
                // Grab it or clone it.  Note that deepClone will
                // add it to the bag.
                val = clonesByIdentity.get(val) ||
Object.deepClone(val, clonesByIdentity);
            }
            result[property] = val;
        }
        return result;
    }
});
* * * * * *

Caveats on that:

1. This is very much off-the-cuff.
2. Same caveat as earlier about the check for whether to clone the
object.
3. I can't imagine it's all that fast.  Offhand I couldn't think of a
faster way to do the SlowIdentityBag; anyone have a l33t idea there?

FWIW,
--
T.J. Crowder
tj / crowder software / com


On Oct 8, 4:18 am, buda <[EMAIL PROTECTED]> wrote:
> I have an object
>
> var obj = {
>   info: {
>     section:{
>       CountryCities:{
>          filter: {
>             CountryID: 'MyCountry'
>          }
>       }
>     }
>   }
>
> }
>
> var arg = Object.clone(obj);
> arg.info.section.CountryCities.CountryID = '100';
>
> or
>
> var arg = Object.extend({}, obj);
> arg.info.section.CountryCities.CountryID = '100';
>
> do the same =>
> arg.info.section.CountryCities.CountryID =
> obj.info.section.CountryCities.CountryID = 100
>
> but I need an independent copy of an obj in arg!!!!!
>
> How could I do it?
--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups 
"Prototype & script.aculo.us" group.
To post to this group, send email to prototype-scriptaculous@googlegroups.com
To unsubscribe from this group, send email to [EMAIL PROTECTED]
For more options, visit this group at 
http://groups.google.com/group/prototype-scriptaculous?hl=en
-~----------~----~----~----~------~----~------~--~---

Reply via email to