We often need to do deep copies of Javascript objects so that we can modify the copy without it affecting the original. I have created the following clone function that does this quickly and easily; however, it does have limitations. If you clone an instance of, lets say, an Ext.Panel, Javascript’s instanceof function will fail to recognize it as an instance of Ext.Panel. From what I can gather, this is because the Javascript engine has some kind of internal reference to an object’s constructor and instanceof doesn’t actually look at the constructor we are able to modify… lets look at an example so it is more clear:
1 2 3 4 5 6 7 8 9 10 11 12 |
var panel = new Ext.Panel(); if(panel instanceof Ext.Panel) console.log("1: panel is an Ext.Panel"); if(panel.constructor == Ext.Panel) console.log("2: panel is an Ext.Panel"); var clonedPanel = com.succinctllc.clone(panel); if(clonedPanel instanceof Ext.Panel) console.log("3: clonedPanel is an Ext.Panel"); if(clonedPanel.constructor == Ext.Panel) console.log("4: clonedPanel is an Ext.Panel"); |
This will output:
1 2 3 |
1: panel is an Ext.Panel 2: panel is an Ext.Panel 4: clonedPanel is an Ext.Panel |
Check #3 fails even though the prototype chain is correct according to check #4. It appears that instanceof relies on some internal magic to do its detection. Once an object is instantiated, it is always a type of that object and cannot be changed. Since our clone function creates an instance of Object it is forever an instanceof Object, even though we changed its prototype chain to match that of an Ext.Panel. I just don’t see another way of doing this. The limitation is not crippling, but you should be cognizant of it.
And without further adieu:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
com.succinctllc.clone = function(obj){ var seenObjects = []; var mappingArray = []; var f = function(simpleObject) { var indexOf = seenObjects.indexOf(simpleObject); if (indexOf == -1) { switch (Ext.type(simpleObject)) { case 'object': seenObjects.push(simpleObject); var newObject = {}; mappingArray.push(newObject); for (var p in simpleObject) newObject[p] = f(simpleObject[p]); newObject.constructor = simpleObject.constructor; return newObject; case 'array': seenObjects.push(simpleObject); var newArray = []; mappingArray.push(newArray); for(var i=0,len=simpleObject.length; i<len; i++) newArray.push(f(simpleObject[i])); return newArray; default: return simpleObject; } } else { return mappingArray[indexOf]; } }; return f(obj); } |
You might also take a look at the Component method cloneConfig() which will make a clone of a Component using the source component’s original config (and also allows new overrides).
Thats a good idea. It will at least make Components instanceof Component. I will work on adding this feature.