ObjectCopy gets no love.
There is more to programming than just syntax (knowing how a language is structured). Being an effective programmer often requires knowing what's available to use - code that are already written for you that you can leverage without having to spend time writing it yourself. In ActionScript 2, this code comes in the form of class libraries. Class libraries often provide functionality that enable you to be more effective and complete your job in a more time efficient manner, as well as provide tools to make solving problems easier.
That being said, I see this question all of the time: "How do I copy an object?." The question stems from the fact that complex types in Flash are reference types. In contrast, primitive types are value types. The primitive types are number, string, boolean, undefined, and null. Everything else is a reference type. When you have a variable that is a reference type, the variable itself doesn't contain the information about the object, rather it just contains a reference to the location of that object in memory. For primitive types, variables actually contain the data as opposed to containing a reference to the data. There are some Computer Science explanations for this that I won't get into here, but I'm sure you can find them on google.
When we attempt to copy an object via normal assignment, what actually happens is that the new variable is set to the reference of the object we're attempting to copy. Instead of making a new object, like what we probably wanted to do, we've just created 2 ways to refer to the same object. A simple example:
var obj:Object = new Object(); obj.color = "red"; obj.count = 5; // our first attempt at coping object var obj2:Object = obj; obj2.color= "blue" trace(obj.color); // blue trace(obj2.color); // blue
When we modify the name in obj2, the name in obj also gets modified. That is because the = assigns a reference to obj2, rather than creating a copy of the object. That is, obj2 and obj refer to the same object in memory.
It doesn't take long for someone to answer the question. Usually it involves some sort of loop that loops over all of the properties inside of the object. Because of the value / reference problem with copying, what you need to do is break the object down into it's primitive types, and then copy those. The primitive types are value types, so when we copy them we get a brand new value in memory that has the same data but is independent of the original value, which is precisely what we want. This is often called a "true copy". To copy the complex reference types, we'll simply create a "new" object, and then store all of the same primitives in it. Creating a new obejct gives us a new reference, so that eliminates the dual reference problem. That code typically looks like this:
function copyObject(obj) {
// create a "new" object or array depending on the type of obj
var copy = (obj instanceof Array) ? [] : {};
// loop over all of the value in the object or the array to copy them
for(var i in obj) {
// assign a temporarity value for the data inside the object
var item = obj[i];
// check to see if the data is complex or primitive
switch(item instanceof Array || item instanceof Object) {
case true:
// if the data inside of the complex type is still complex, we need to
// break that down further, so call copyObject again on that complex
// item
copy[i] = copyObject(item);
break;
default:
// the data inside is primitive, so just copy it (this is a value copy)
copy[i] = item;
}
}
return copy;
}
Sample usage:
var obj:Object = new Object(); obj.color = "red"; obj.count = 5; // our second attempt at coping object var obj2:Object = copyObject(obj); obj2.color= "blue" trace(obj.color); // red trace(obj2.color); // blue
As you can see, this method worked great. We now can copy an object without having to worry about any of the "reference stuff." However, there's a problem with this code. Do you know what it is? It's not obvious at first, but if you try to copy an instance of a custom class, the code doesn't work quite right. Why? Because the copy of the object will not be an instance of the class, since when we copy the object we never call the class constructor! An example of that:
// Dog.as
class Dog {
public var name:String;
public function Dog(name:String) {
this.name = name;
}
public function speak():String {
return name + " says, 'Arf arf!'";
}
}
// in a .fla file:
var dog1:Dog = new Dog("Yelowdog");
// clone Yellowdog because my puppy is just that cute
var dog2:Dog = copyObject(dog1);
dog2.name = "Yellowdog Clone";
trace( dog1.speak() ); // Yelowdog says, 'Arf arf!'
trace( dog2.name ); // Yellowdog Clone
trace( dog2.speak() ); // undefined
trace( dog2 instanceof Dog ); // false
You can see that the copy "kind of" worked. That is, modifying the name of dog2 didn't change the name of dog1. However, in the copy process we lost all of the information related to our class, and you can see that dog2 is not an instance of Dog after the copy.
How do we fix this? The answer is simple - we don't. Instead, lets use the tools that are available to us. This problem has long been a problem, and as such there are already solutions available, right under our nose too. All we need to do is look at one of the classes that ship with Flash MX 2004. It's mx.utils.ObjectCopy, and it's very simple to use and hides all of that "reference stuff" from us so we don't have to deal with it.
import mx.utils.ObjectCopy;
var dog1:Dog = new Dog("Yelowdog");
// use ObjectCopy to do the copy, and because we know the result
// is going to be a Dog, use the Dog cast so we don't get compiler errors
var dog2:Dog = Dog( ObjectCopy.copy(dog1) );
dog2.name = "Yellowdog Clone";
trace( dog1.speak() ); // Yelowdog says, 'Arf arf!'
trace( dog2.name ); // Yellowdog Clone
trace( dog2.speak() ); // Yellowdog Clone says, 'Arf arf!'
trace( dog2 instanceof Dog ); // true
Moral of the story: While it's good to reinvent the wheel if you're interested in the learning process, if you're just interested in the results of what the wheel does, you can leverage class libraries to re-use code already written. Flash MX 2004 ships with a decent amount of classes which you can find on Windows in somewhere like C:\Program Files\Macromedia\Flash MX 2004\en\First Run\Classes. There are also class libraries available on-line. Here's a short list, in no particular order
- http://www.as2lib.org/
- http://www.person13.com/ascblibrary/
- http://proto.layer51.com/ - AS1 prototype extensions
- http://members.shaw.ca/flashprogramming/wisASLibrary/wis/index.html
- http://actioncrypt.sourceforge.net/
- http://www.aslib.org/
I'm sure there are other lass libraries as well (feel free to add more in the comments). Note that because ActionScript and JavaScript are very similar, chances are you can re-use code from JavaScript libraries without much modification. In fact, a lot of JavaScript code requires nothing more than a copy/paste into Flash for it to work.
As always, enjoy exploring...
UPDATE: It seems as though you need to download the Flash Remoting Soure Code to use the ObjectCopy class. I've been doing this for so long that I thought it shipped with Flash. Sorry about that!
