Connecting Tech Pros Worldwide Help | Site Map

Deep Cloning JS-Objects

gits's Avatar
Moderator
 
Join Date: May 2007
Location: Munich, Germany
Posts: 4,126
#1   Oct 4 '07
This short article introduces a method that may be used to create a 'deep-copy' of an javascript object. You might ask: 'Wherefore do we need this?' ... Answer: 'Only variable-values of the basic data-types string, int, float, boolean and to make the confusion perfect :) functions too! are passed by value, all others are passed by reference.' This means, when you create an object a, like in the code below, and you assign object a to variable b then you will pass the object a by reference. That results in the fact, that when you assign a new value to b.foobar, a.foobar will be changed accordingly. To pass it by value, what you can imagine as a (deep-)copy of object a, we have to use a function that recursivly copies the values of any datastructure inside of object a to object b.

Expand|Select|Wrap|Line Numbers
  1. // example object that might be cloned
  2. var a = {
  3.    foo   : { test: 1, test1: 2 },
  4.    bar   : function(a) { alert(a) },
  5.    foobar: 'foobar1',
  6.    arr   : [1, [8, 9], {1: 1, 2: 2}, 4]
  7. };
  8.  
  9. /**
  10.  * function clone_obj deep-clones an js-object
  11.  *
  12.  * @param obj javascript-object
  13.  * @return c cloned javascript-object
  14.  */
  15. function clone_obj(obj) {
  16.     if (typeof obj !== 'object' || obj == null) {
  17.         return obj;
  18.     }
  19.  
  20.     var c = obj instanceof Array ? [] : {};
  21.  
  22.     for (var i in obj) {
  23.         var prop = obj[i];
  24.  
  25.         if (typeof prop == 'object') {
  26.            if (prop instanceof Array) {
  27.                c[i] = [];
  28.  
  29.                for (var j = 0; j < prop.length; j++) {
  30.                    if (typeof prop[j] != 'object') {
  31.                        c[i].push(prop[j]);
  32.                    } else {
  33.                        c[i].push(clone_obj(prop[j]));
  34.                    }
  35.                }
  36.            } else {
  37.                c[i] = clone_obj(prop);
  38.            }
  39.         } else {
  40.            c[i] = prop;
  41.         }
  42.     }
  43.  
  44.     return c;
  45. }
  46.  
  47. // usage: we clone object a to object b
  48. var b = clone_obj(a);
  49.  
Note: Why can't we copy dom-nodes with that function? Answer: dom-nodes contain references to its parent- and child-objects etc. When copying this with the above method we will get an error: 'too much recursion' ... because when copying a parent object we come to a child of that and from there to its parent and so on ... for cloning dom-nodes we simply may use the cloneNode() dom-method.

testers needed ;) ... i think it works the way it should ... but may be I've overlooked something?

testing-help: when testing in firebug console the test scenario could be for example:

Expand|Select|Wrap|Line Numbers
  1. var a = {
  2.    foo   : { test: 1, test1: 2 },
  3.    bar   : function(a) { alert(a) },
  4.    foobar: 'foobar1',
  5.    arr   : [1, [8, 9], {1: 1, 2: 2}, 4]
  6. };
  7.  
  8. var c = a;
  9. c.foobar = 'new test';
  10.  
  11. function clone_obj(obj) {
  12.     if (typeof obj !== 'object' || obj == null) {
  13.         return obj;
  14.     }
  15.  
  16.     var c = obj instanceof Array ? [] : {};
  17.  
  18.     for (var i in obj) {
  19.         var prop = obj[i];
  20.  
  21.         if (typeof prop == 'object') {
  22.            if (prop instanceof Array) {
  23.                c[i] = [];
  24.  
  25.                for (var j = 0; j < prop.length; j++) {
  26.                    if (typeof prop[j] != 'object') {
  27.                        c[i].push(prop[j]);
  28.                    } else {
  29.                        c[i].push(clone_obj(prop[j]));
  30.                    }
  31.                }
  32.            } else {
  33.                c[i] = clone_obj(prop);
  34.            }
  35.         } else {
  36.            c[i] = prop;
  37.         }
  38.     }
  39.  
  40.     return c;
  41. }
  42.  
  43. var b = clone_obj(a);
  44. b.foobar = 'test';
  45. b.bar = function(a) { alert(a + a) };
  46.  
  47. console.info(a.toSource());
  48. console.info(b.toSource());
  49.  

Last edited by gits; Oct 15 '07 at 12:52 PM. Reason: fix try of clone basic data-types



Reply


Similar JavaScript / Ajax / DHTML bytes