8

I see two ways to duplicate objects

1.

var a={c:1}
var b=a;
alert(b.c);//alert 1

2.

var a={c:2};
var b={};
for (i in a)
{b[i]=a[i];} 
alert(b.c);//alert 1

The first are shorter than the second so what is the efficiency in the second example?

user1801625
  • 1,153
  • 3
  • 13
  • 14
  • Fyi, you are supposed to use `var i` in the second example to avoid creating a global variable. Also, `i` is a bad name for a non-numeric variable; I'd use `key` instead. – ThiefMaster Nov 08 '12 at 10:49
  • 3
    Because people expect `i` to be a numeric loop variable in a `for(i = 0; ...; i++)`-style loop, i.e. to iterate over an *array*. – ThiefMaster Nov 08 '12 at 10:52
  • 1
    possible duplicate of [What is the most efficient way to clone a JavaScript object?](http://stackoverflow.com/questions/122102/what-is-the-most-efficient-way-to-clone-a-javascript-object) – Andreas Louv Nov 08 '12 at 11:08
  • Does this answer your question? [What is the most efficient way to deep clone an object in JavaScript?](https://stackoverflow.com/questions/122102/what-is-the-most-efficient-way-to-deep-clone-an-object-in-javascript) – Jacksonkr Mar 22 '21 at 19:25

4 Answers4

13

In the first version you don't duplicate/clone the object you simply make a extra reference to it:

var a = { a: 1 };
var b = a;
b.a = 2;

console.log(a.a); // 2;

To clone an object there is numbers of libraries that can do that for you:

var b = $.extend({}, a); // Make a shallow clone (jQuery)
var b _.extend({}, a); // Make a shallow clone (underscore.js)

var b = $.extend(true, {}, a); // Make a deep clone (jQuery);

Or you can do it natively:
Simple clone:

var b = {};
var prop;

for (prop in a) {
    b[prop] = a[prop];
}

Scratch of a deep clone function:

function deepClone(obj) {
    var r;
    var i = 0,
    var len = obj.length;
    // string, number, boolean
    if (typeof obj !== "object") { 
        r = obj;
    }
    // Simple check for array
    else if ( len ) { 
        r = [];
        for ( ; i < len; i++ ) {
            r.push( deepClone(obj[i]) );
        }
    } 
    // Simple check for date
    else if ( obj.getTime ) { 
        r = new Date( +obj );
    }
    // Simple check for DOM node
    else if ( obj.nodeName ) { 
        r = obj;
    }
    // Object
    else { 
        r = {};
        for (i in obj) {
            r[i] = deepClone(obj[i]);
        }
    }

    return r;
}
Andreas Louv
  • 46,145
  • 13
  • 104
  • 123
  • checking for a length property won't cut the mustard: `arguments` have a length property, too, as does a `NodeList` and various other objects. An object literal might have a length property, too, for example – Elias Van Ootegem Nov 08 '12 at 11:12
8

The first does not create a copy, but just copies the reference, so a and b point towards the same object after the operation.

In the second case, however, each attribute is copied separately, thus creating a "real" copy of the object in a (as long as there are just primitive types in the properties, else you got the same problem at a deeper level).

So in the first case if you change b.c then a.c will also change, while in the second case it wont.

Sirko
  • 72,589
  • 19
  • 149
  • 183
4

As others have stated here: the first assignment, assigns a new reference to an existing object, the second performs a shallow copy.
By shallow I mean: only the base object will be copied, there is no recursion:

var a = {some:'propery',
         another:{might:'be',
                  an: 'object, too'}
        };
var b = {};
for(var p in a)
{
    b[p] = a[p];
}
b.some = 'b\'s own property';
console.log(a.some);//property -> unaltered
console.log(b.some);//b's own property --> separate entities
b.another.might = 'foo';
console.log(a.another.might);//foo ==> b.another references a.another

To solve this issue, you would be forgiven to think that a simple recursive function would suffice:

var cloneObj = function(o)
{
    var p,r = {};
    for (p in o)
    {//omitting checks for functions, date objects and the like
        r[p] = (o[p] instanceof Object ? cloneObj(o[p]) : o[p]);
    }
};

But be weary of circular references!

//assume a is the same object as above
a._myself = a;//<- a references itself

This will produce endless recursion, aka a deadlock scenario, unless you add a check for just such cases:

var cloneObj = function(o)
{
    var p,r = {};
    for (p in o)
    {//Needs a lot more work, just a basic example of a recursive copy function
        switch(true)
        {
            case o[p] instanceof Function:
                r[p] = o[p];
            break;
            case o[p] instanceof Date:
                r[p] = new Date(o[p]);
            break;
            case o === o[p]:
            //simple circular references only
            //a.some.child.object.references = a; will still cause trouble
                r[p] = r;
            break;
            case o[p] instanceof Array:
                r[p] = o[p].slice(0);//copy arrays
            break;
            default:
                r[p] = o[p] instanceof Object ? cloneObj(o[p]) : o[p];
        }
    }
    return r;
};

Now, this is quite verbose, and in most cases utter overkill, if all you want are two objects with the same data, but can be altered independently (ie don't reference the same object in memory), all you need is 1 line of code:

var a = {some:'propery',
         another:{might:'be',
                  an: 'object, too'}
        };
var b = JSON.parse(JSON.stringify(a));

Bottom line: assigning a reference is certainly more efficient: it doesn't require the object constructor to be called a second time, nor does it require an additional copy of any of the constants.
The downside is: you end up with a single object and might inadvertently change/delete something that you assume is still there when you're using the other reference (delete b.some;/*some time later*/a.some.replace(/p/g,'q');//<--error)

Elias Van Ootegem
  • 74,482
  • 9
  • 111
  • 149
3

The first copies the reference and does not duplicate the object, the second creates a new reference and then copies the members (which, in turn, if they are references will just copy the references only).

You might want to look at this other SO - Does Javascript equal sign reference objects or clones them?

It's not a question of efficiency it's ultimately about correctness. If you need to share a reference to an object between different code blocks (e.g. so that multiple pieces of code can share the same object) - then you simply rely on the fact that javascript passes by reference.

If, however, you need to copy an object between methods - then you might be able to use your simple example in your second code block (if your objects don't have other 'objects' in them), otherwise you might have to implement a deep-clone (see How to Deep clone in javascript, and pay attention to the answer(s) there - it's not a trivial business).

Community
  • 1
  • 1
Andras Zoltan
  • 41,961
  • 13
  • 104
  • 160