How to Clone Objects in AS3

Cloning simple objects in AS3 can have great advantages when creating an application. It creates shortcuts for recreating an object. I have put together a simple example of my exploration with cloning TextFormat and TextField objects. These methods can be applied to cloning other objects as well.

Cloning a TextFormat

Have you had projects where you were using the same basic TextFormat for your textfields and wanted to base sub-textformats on a base format and change a few properties? This is something I found I was needing all the time where I had one instance of baseTextFormat with the basic attributes and wanted to say myOtherFormat:TextFormat=baseTextFormat. The problem with this is when you change formatting for myOtherFormat without it being a clone of the first, it will change the formatting on the baseTextFormat, which is obviously not what is wanted.

So, the solution here is to create a clone of the first by utilizing the ByteArray class:

function cloneFormat(o:*):* {
	var obj:Object = getByteArray(o); // this returns null on MovieClips
	return obj;
}
function getByteArray(obj:Object){
	var byteArray:ByteArray=new ByteArray();
	byteArray.writeObject(obj);
	byteArray.position = 0;
	return byteArray.readObject()
}

The above method returns a basic object, so in order to parse it to a TextFormat object you will need add a parsing method that will loop through each object of the byteArray and put it in a new TextFormat:

function cloneFormat(o:*):* {
	var obj:Object = getByteArray(o); // this returns null on MovieClips
	obj = parseFormat(obj); // parse the object to a TextFormat
	return obj;
}
function parseFormat(obj):TextFormat{
	var _tf:TextFormat=new TextFormat();
	for (var prop:String in obj){
		//trace(prop)
		_tf[prop]=obj[prop]
	}
	return _tf
}

Here is some basic code example to clone the baseFormat:

// setup your baseFormat
var baseFormat:TextFormat = new TextFormat();
baseFormat.size=20;
baseFormat.font="_sans";
baseFormat.color=0xff0000;
var my_txt:TextField = new TextField();
my_txt.defaultTextFormat = baseFormat; // apply the format
my_txt.text="Hello World";
addChild(my_txt);
var my_txt2:TextField = new TextField();
my_txt2.width=300;
my_txt2.wordWrap=true;
my_txt2.multiline=true;
my_txt2.autoSize=TextFieldAutoSize.LEFT;
// clone the baseFormat
var myFormat2 = cloneFormat(baseFormat)
// change format slightly to see difference
myFormat2.color=0x005599;
// apply the format
my_txt2.defaultTextFormat=myFormat2
my_txt2.text="Here the text uses format from the first was cloned with a color variation"
addChild(my_txt2);

Cloning a TextField

Cloning a TextField is almost as easy as cloning the TextFormat. It requires a couple minor adjustments where the defaultTextFormat and transform properties need additional parsing:

function parseTextField(_obj:Object, _tf:TextField):TextField{
	var _textField:TextField =new TextField();
	for (var prop:String in _obj){
		if (prop=="transform"){
			_textField[prop] =new Transform(_tf);
		}else if (prop=="defaultTextFormat"){
			_textField[prop] = parseFormat(_obj[prop]);
		}else{
			try{
				_textField[prop]=_obj[prop]
			}catch(err:Error){
				trace("Error Hit: "+err)
			}
		}
	}
	return _textField
}

Setting Up a Clone Class

To create this as a class to handle the passed parameters isn’t too difficult. I have added a couple checks to see what type of object gets passed so that it parses the objects according to their needed requirements. Here is my class code, use at your own discretion and know that it is not fully tested or supported:

package {
	import flash.utils.ByteArray;
	import flash.text.TextFormat;
	import flash.text.TextField;
	import flash.utils.ByteArray;
	import flash.utils.getQualifiedClassName;
	import flash.geom.Transform;
	import flash.display.DisplayObject;
	public class Clone {
		public function Clone(o:Object) {
			// constructor code	
		}
		public static function clone(o:*):* {
			var type = getClassName(o);
			var obj:Object = getByteArray(o); // this returns null on MovieClips
			switch(type){
				case "TextFormat":
				default:
					obj = parseFormat(obj);
					break;
				case "TextField":
					obj = parseTextField(obj,o);
					break;
			}
			return obj;
		}
		private static function getByteArray(obj:Object){
			var byteArray:ByteArray=new ByteArray();
			byteArray.writeObject(obj);
			byteArray.position = 0;
			return byteArray.readObject()
		}
		private static function getClassName(o:Object):String{
       		var fullClassName:String = getQualifiedClassName(o);
			return fullClassName.slice(fullClassName.lastIndexOf("::") + 2);
		}
		private static function parseFormat(_obj):TextFormat{
			var _tf:TextFormat=new TextFormat();
			for (var prop:String in _obj){
				//trace(prop)
				_tf[prop]=_obj[prop]
			}
			return _tf
		}
		private static function parseTextField(_obj:Object,o:TextField):TextField{
			var _textField:TextField =new TextField();
			for (var prop:String in _obj){
				if (prop=="transform"){
					// actually setting transform properties becomes complex
					_textField[prop] =new Transform(o);
				}else if (prop=="defaultTextFormat"){
					_textField[prop] = parseFormat(_obj[prop]);
				}else{
					try{
						_textField[prop]=_obj[prop]
					}catch(err:Error){
						trace("Error Hit: "+err)
					}
				}
			}
			return _textField
		}
	}
}

Cloning Other Objects

With my Clone class there are additional possibilities besides the cloning of the described objects. It becomes more difficult when attempting to clone more complex display objects such as MovieClips. If the content within the MovieClip or Sprite is more dynamic and created at runtime, it may not simply be re-instantiated from the library as a new instance. Therefore, a different approach is needed to duplicating your MovieClip. You could use the BitmapData class instead if it is only necessary to duplicate the pixels. However, this is not always what is wanted, especially if the MovieClip has animation. If you attempt using the ByteArray method for returning the more complex Matrix of values a MovieClip contains, you end up with null. I will not get into the complexities of it here but a good example was given within the Kirupa forums by Senocular. His method works, but he does explain some of the limitations of his method.

Try it out for yourself. You will find some great use out of my Clone Class and perhaps you may find great ways to extend it. If you have any brilliant other uses of cloning objects using my Class or other approaches, please don’t forget to leave a comment. Thank you for your feedback.