Clone() In JavaScript
I recently did a google search for "javascript clone", and noticed there's a lot of confusion about cloning vs. copying out there. Every function called clone() I found was in fact either a shallow or deep copy. All well and good (but not as good as my copy(), see below) but not actually a "clone," really. I blame Java for this; the copy method on Java's Object is called clone(). But notice how they describe it in their documentation:
clone()
Creates and returns a copy of this object.
When the first word that comes to mind to describe a method is different than the method's name, something has gone wrong. I would expect clone() to mean what it means in SELF and Io: a function that returns a new object with the original object somewhere up the scope chain. Unlike those languages, there is no native clone() function in JavaScript, but because JavaScript is a prototype-based langauge it's possible to replicate that behavior exactly, elegantly, and easily. (Sorry for the e-lliteration.)
To go along with this discussion, here's a little library of JavaScript utilities:
It also includes a quite thorough shallow copy() function that handles native types and objects, and if you accept the Java definition of clone that's probably what you were looking for. The "real" clone() is far more interesting though, and it may very well be what you should be using, so read on.
The Prototype Relationship
You already know that JavaScript objects are collections of key-value pairs; in other words maps or "hash tables". But you also know that isn't the whole story, because core Objects like String have an actual value somewhere deep inside them, and objects have a somewhat magical relationship with their constructors and prototypes. A JavaScript object really is a collection of key-value pairs plus some special internal data.
One of those data is the prototype; every JavaScript object has an internal reference to the object it was spawned from, all the way up to the root Object. The prototype is very special: when I look up a key in a given object, JavaScript first looks for it in the object itself, and if it isn't there it looks for it in its prototype, and then in the prototype's prototype, and so on until I either find the key or run into the root Object. In other words, the prototypes form a "scope chain."
We're encouraged to think of this as the "subclass", or "is-a", relationship by modern Object Oriented Programming, but this isn't entirely right. Consider Hofstadter's discussion of how ideas relate to each other in "Classes and Instances" from Godel, Escher, Bach: An Eternal Golden Braid:
It might seem at first sight that a given symbol would inherently
be either a symbol for a class or a symbol for an instance - but
that in an oversimplification. Actually most symbols may play
either role, depending on the context of their activation. For
example, look at the list below:
(1) a publication
(2) a newspaper
(3) The San Francisco Chronicle
(4) the May 18th edition of the Chronicle
(5) my copy of the May 18th edition of the Chronicle
(6) my copy as it was a few days later[...]
He then goes on to talk about the "The Prototype Principle," and "The Splitting-off of Instances from Classes," making the point that anything can be considered specific or abstract, from some point of view. This isn't computer science specifically (although Hofstadter is an AI researcher) but a philosophical attempt to understand how concepts really interact.
The way I see it, the whole point of the class/object dichotomy is ease-of-implementation and performance in static languages. In a static language like C++, class information is (mostly) used at compile time and is expressed in generated machine code, while an object is just a sequential block of memory. Concepts like scope and class don't really exist at runtime; they are just implicit in the generated machine code. So we're not going to get away with treating an object as a class in a static language. Dynamic languages, like JavaScript, are a different story: here, scope and inheritance are already runtime concepts, so we have more flexibility. Since we don't have to worry about implementing a good compiler, we are free to pursue inheritance models that may more closely resemble the human thought process.
Clone And You
All we need to take away from all this ivory-tower stuff is that there may not be a clean line between "classes" and "instances;" and in fact examples pop up quite often of wanting to extend or locally modify an existing object. The "scope chain" between global and local variables in most languages is isomorphic to the prototype relationship. When Oracle opens up a transaction for you, it's just like you're writing to a clone (which then get's applied to the original when you commit.) SVN's "cheap copies" are another kind of clone. It's really a very common and powerful idea.
So here's what clone() can do for you: You can take any existing object and tear-off your own version of it... but if the original get's modified, so will the clone, whereas changes to the clone never propogate back to the original. It's a little bit like laying a transparency over a piece of paper and drawing on it with a marker.
Clone() in JavaScript
Here's my version of the clone() function in JavaScript, from owl_util.js
function clone(obj) {
// A clone of an object is an empty object
// with a prototype reference to the original.
// a private constructor, used only by this one clone.
function Clone() { }
Clone.prototype = obj;
return new Clone();
}
Since JavaScript always hangs prototypes off a constructor, I simply create a different, private constructor for each clone. In that sense, each clone is it's own class. (I suppose I could re-use the same class/constructor for different clones of the same object, but it's not worth keeping track of.)
The net result of this trick is that I can clone any object, and the clone will have the prototype relationship with its original.
var original = { a:'A', b:'B' };
var clone = owl.util.clone(original);
// clone.a == 'A'
// clone.b == 'B'
clone.a = 'Apple';
clone.a == 'Apple'
// original.a == 'A' // unchanged
original.b = 'Banana'
// clone.b == 'Banana' // change shows through
clone.c = 'Car'
// original.c is undefined
original.a = 'Abracadabra'
// clone.a == 'Apple' // clone's new value hides the original's
delete clone.a
// clone.a = 'Abracadabra' // original value visible again
// repeating "delete clone.a" won't delete the original's value.
The clone() in owl_utils.js also keeps track of the original, which can sometimes be useful. The other functions in owl_utils.js are there to show how one might use this. If you want to play with it yourself, I recommend Firebug's command line console.
Clone != Shallow Copy != Deep Copy
I hope I've convinced you that the cloning an object is a very different thing that making a copy, deep or shallow. It means giving the clone the prototype or "is-a" relationship. My implementation probably isn't perfect, but I wanted to get it out there to remind people that in dynamic languages, cloning is a distinct and extremely interesting operation.
Also
Mad props to David Flanagan for JavaScript: The Definitive Guide 5th Edition, the best book on JavaScript I've ever seen and the source of much wisdom.
If you liked this essay, you might be interested in these: [ Coding Style Web Tutorial Popular ]
by Oran Looney - January 23rd 2008
