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.
[CODE=javascript]
// example object that might be cloned
var a = {
foo : { test: 1, test1: 2 },
bar : function(a) { alert(a) },
foobar: 'foobar1',
arr : [1, [8, 9], {1: 1, 2: 2}, 4]
};
/**
* function clone_obj deep-clones an js-object
*
* @param obj javascript-object
* @return c cloned javascript-object
*/
function clone_obj(obj) {
if (typeof obj !== 'object' || obj == null) {
return obj;
}
var c = obj instanceof Array ? [] : {};
for (var i in obj) {
var prop = obj[i];
if (typeof prop == 'object') {
if (prop instanceof Array) {
c[i] = [];
for (var j = 0; j < prop.length; j++) {
if (typeof prop[j] != 'object') {
c[i].push(prop[j]);
} else {
c[i].push(clone_obj (prop[j]));
}
}
} else {
c[i] = clone_obj(prop) ;
}
} else {
c[i] = prop;
}
}
return c;
}
// usage: we clone object a to object b
var b = clone_obj(a);
[/CODE]
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:
[CODE=javascript]var a = {
foo : { test: 1, test1: 2 },
bar : function(a) { alert(a) },
foobar: 'foobar1',
arr : [1, [8, 9], {1: 1, 2: 2}, 4]
};
var c = a;
c.foobar = 'new test';
function clone_obj(obj) {
if (typeof obj !== 'object' || obj == null) {
return obj;
}
var c = obj instanceof Array ? [] : {};
for (var i in obj) {
var prop = obj[i];
if (typeof prop == 'object') {
if (prop instanceof Array) {
c[i] = [];
for (var j = 0; j < prop.length; j++) {
if (typeof prop[j] != 'object') {
c[i].push(prop[j]);
} else {
c[i].push(clone_obj (prop[j]));
}
}
} else {
c[i] = clone_obj(prop) ;
}
} else {
c[i] = prop;
}
}
return c;
}
var b = clone_obj(a);
b.foobar = 'test';
b.bar = function(a) { alert(a + a) };
console.info(a. toSource());
console.info(b. toSource());
[/CODE]
[CODE=javascript]
// example object that might be cloned
var a = {
foo : { test: 1, test1: 2 },
bar : function(a) { alert(a) },
foobar: 'foobar1',
arr : [1, [8, 9], {1: 1, 2: 2}, 4]
};
/**
* function clone_obj deep-clones an js-object
*
* @param obj javascript-object
* @return c cloned javascript-object
*/
function clone_obj(obj) {
if (typeof obj !== 'object' || obj == null) {
return obj;
}
var c = obj instanceof Array ? [] : {};
for (var i in obj) {
var prop = obj[i];
if (typeof prop == 'object') {
if (prop instanceof Array) {
c[i] = [];
for (var j = 0; j < prop.length; j++) {
if (typeof prop[j] != 'object') {
c[i].push(prop[j]);
} else {
c[i].push(clone_obj (prop[j]));
}
}
} else {
c[i] = clone_obj(prop) ;
}
} else {
c[i] = prop;
}
}
return c;
}
// usage: we clone object a to object b
var b = clone_obj(a);
[/CODE]
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:
[CODE=javascript]var a = {
foo : { test: 1, test1: 2 },
bar : function(a) { alert(a) },
foobar: 'foobar1',
arr : [1, [8, 9], {1: 1, 2: 2}, 4]
};
var c = a;
c.foobar = 'new test';
function clone_obj(obj) {
if (typeof obj !== 'object' || obj == null) {
return obj;
}
var c = obj instanceof Array ? [] : {};
for (var i in obj) {
var prop = obj[i];
if (typeof prop == 'object') {
if (prop instanceof Array) {
c[i] = [];
for (var j = 0; j < prop.length; j++) {
if (typeof prop[j] != 'object') {
c[i].push(prop[j]);
} else {
c[i].push(clone_obj (prop[j]));
}
}
} else {
c[i] = clone_obj(prop) ;
}
} else {
c[i] = prop;
}
}
return c;
}
var b = clone_obj(a);
b.foobar = 'test';
b.bar = function(a) { alert(a + a) };
console.info(a. toSource());
console.info(b. toSource());
[/CODE]
Comment