在AS3中,如何实现数组及对象的深拷贝?

本文详细介绍了在ActionScript 3中实现数组深拷贝和对象复制的方法,包括使用concat、slice、for循环以及ByteArray。特别强调了如何利用ByteArray实现对象的深拷贝,同时揭示了在FlashPlayer中使用ByteArray复制显示对象的局限性。最后,通过ICloneable接口和Array.prototype扩展展示了实现所有对象深拷贝的最佳实践。
摘要由CSDN通过智能技术生成
大概谈了以下4个简单的问题,如果读者对它们的答案都了如指掌,大可不必看了。

1)在AS3中实现数组的复制共有几种方法?

2)如何使用ByteArray实现数组的深拷贝?

3)如何实现所有对象的深拷贝?

4)为什么在Flash Player中不能使用ByteArray拷贝显示对象?

 

 

1,使用concat复制数组

var arr1 :Array = "as3 expert programming by sban".split(/ /);
var arr2 :Array = arr1.concat();
trace(arr2.length, arr2);//5 as3,expert,programming,by,sban

concat的本意是连合两个旧数组的元素,并返回新数组。新数组元素依次按旧数组1,旧数组2的元素排列。当旧数组2为空参时,可返回旧数组1的拷贝。但它的拷贝是浅拷贝,看如下代码:

var arr1 :Array = "as3 expert programming by sban".split(/ /);
arr1.unshift({count:1});
var arr2 :Array = arr1.concat();
arr2[0].count++;
trace(arr2[0].count, arr1[0].count);//2 2

上例代码使用unshift在数组arr1底部推入一个Object对象,它具有一个count属性,由于concat仅是拷贝数组元素的引用,所以arr2[0].count与arr1[0].count的值是相同的。

那么在AS3中,哪些对象是引用对象,哪些是值对象?有哪些对象作为数组元素时,是引用类型?

2,使用slice返回新数组

var arr1 :Array = "as3 expert programming by sban".split(/ /);
var arr2 :Array = arr1.slice();
trace(arr2);//as3,expert,programming,by,sban

slice用于从指定索引开始分割旧数组,以返回新数组,可用于数组拷贝。与concat类似,slice仍是浅拷贝。那么,如果实现数组元素的深拷贝呢?

注:浅拷贝指仅拷贝引用,而深拷贝与之相反,深拷贝结果中的对象与源对象不是同一个对象。在AS3的数组拷贝中,对于值对象,一定是拷贝数据;对于引用对象,一定是拷贝引用。

3,使用for关键字复制数组

var arr1 :Array = "as3 expert programming by sban".split(/ /);
var arr2 :Array = new Array(arr1.length);
var n :int = arr1.length;
for(var j:int=0; j<n; j++)
{
	arr2[j] = arr1[j];
}
trace(arr2);//as3,expert,programming,by,sban

使用for关键字复制数组元素,在这里毫无疑问也是引用复制(对于引用对象类型)。值得提出的是,使用for关键字并不能保证可以遍历出所有数组元素,因为也不能保证复制的有效性。

为什么说for不能保证遍历数组的所有元素?

除了使用for,使用for..in或for each in或while同样也可以实现类似的数组复制,初学者可以自行实现一下练练手。

4,如何使用ByteArray实现数组的深拷贝

有时候,我们希望在修改新拷贝的数组元素时,不会对源数组产生任何影响,这便需要深拷贝。

在AS3中可以使用ByteArray复制源对象,产生新对象,以下是Adobe在Livedoc中提供的引用对象深拷贝方法:

public function clone(source:Object): Object
{
	var r: ByteArray = new ByteArray();
	r.writeObject(source);
	r.position = 0;

	return (r.readObject());
}

运用该方法修改前面的拷贝示例,以实现数组元素的深拷贝,代码示例如下:

var arr1 :Array = "as3 expert programming by sban".split(/ /);
arr1.unshift({count:1});
var arr2 :Array = clone(arr1) as Array;
arr2[0].count++;
trace(arr2.length, arr2);//6 [object Object],as3,expert,programming,by,sban
trace(arr2[0].count, arr1[0].count);//2 1

从输出结果可以看出,在该示例中,arr2[0]与arr1[0]便不再是同一个对象。貌似这样我们就可以实现对象的深拷贝了,但这个方法是有局限的。它适用于在AS3中定义的数据类型,包括所有基元类型及由基元类型组合的复合类型,却并适用于在Flash Player中定义的显示对象类型,如Sprite,MovieClip等。

注:什么是基元类型?指String, Number, uint, int, Object,Null, Boolean,void。

下面我们用实验的方法,验证Adobe提供的ByteArray clone方法不能拷贝Flash Player中定义的显示对象。首先,sban定义一个TextFieldSprite对象,它继承于Sprite,主要代码如下:

public class TextFieldSprite extends Sprite
{
	public function TextFieldSprite()
	{
		var t :TextField = new TextField();
		t.text = 'as3 expert programming by sban';
		t.autoSize = TextFieldAutoSize.LEFT;
		addChild(t);
	}
}

这个代码很简单,仅是在构造时添加一个文本。接下来,使用前面类似方式,实例化一个数组,然后进行复制:

var arr1 :Array = [new TextFieldSprite(), new TextFieldSprite()];
var arr2 :Array = clone( arr1 ) as Array;
trace(arr2);//[object Object],[object Object]

代码运行到这里,还相安无事。由输出可以看出,貌似我们的深拷贝成功了。但问题是,深拷贝出来的对象却是不能使用的,这里的不能使用指它们不能被添加进显示列表中。当sban试图把它们添加进显示列表,代码示例如下:

arr2.forEach(
	function(item :DisplayObject, index :int=-1, arr :Array = null) :void
	{
		addChild(item);
	}
);

这段时间在编译时没有任何问题,在运行时则会引发如下异常:

TypeError: Error #1034: 强制转换类型失败:无法将 Object@228bd09 转换为 flash.display.DisplayObject。
	at Array$/_forEach()
	at Array/http://adobe.com/AS3/2006/builtin::forEach()
	at Main()[C:\Users\virgo\Documents\ria\ad3_fd\src\Main.as:22]

 

实验证明,在AS3中,不能使用ByteArray深拷贝显示对象。

那么,为什么在AS3中不能使用ByteArray拷贝显示对象?使用它拷贝出来的对象为什么不能转换为DisplayObject?

5,如何实现所有对象的深拷贝?

使用ByteArray仅能在一定范围内实现数组的深拷贝,那么我们应当如何实现所有对象的深拷贝呢?

目前在AS3中,没有一个像ByteArray clone那么简单而又适用所有类型的数组深拷贝方法。在项目开发中,我们需要定义一个IClonaeble接口:

package sban.as3Expert.interfaces
{
	/**
	 *
	 * @author sban <http://sban.biz/>
	 * Gmail: sban.net@gmail.com
	 *
	 * 2010-4-24
	 *
	 */
	public interface ICloneable
	{
		function clone() :ICloneable;
	}
}

然后让可能需要深拷贝的每一个对象实现这个接口,在方法clone中new一个新对象,并把当前对象的数据一一赋值给新对象。例如修改前面用到的TextFieldSprite对象,如下:

package sban.as3Expert
{
	import flash.display.Sprite;
	import flash.text.TextField;
	import flash.text.TextFieldAutoSize;
	import flash.text.TextFormat;

	import sban.as3Expert.interfaces.ICloneable;

	/**
	 *
	 * @author sban <http://sban.biz/>
	 * Gmail: sban.net@gmail.com
	 *
	 * 2010-4-24
	 *
	 */
	public class TextFieldSprite extends Sprite implements ICloneable
	{
		public function TextFieldSprite()
		{
			super();

			t = new TextField();
			t.text = 'as3 expert programming';
			t.autoSize = TextFieldAutoSize.LEFT;
			addChild(t);
		}

		private var t :TextField;

		public function setText(s :String) :void
		{
			t.text = s;
		}

		public function getText() : String
		{
			return t.text;
		}

		public function clone() :ICloneable
		{
			var r :TextFieldSprite = new TextFieldSprite();
			r.setText( this.getText());

			return r;
		}
	}
}

调用其clone方法用于深拷贝该对象,每一个对象负责其本身的clone方法的具体实现,因为只有它自己知道它有哪些数据需要被复制。

提示:面向对象编程一条很重要的原则便是,让对象做只有它自己才知道的事情。

复制TextFieldSprite对象的代码示例:

var t1 :TextFieldSprite = new TextFieldSprite();
t1.setText( "TextFieldSprite 1" );
addChild( t1 );

var t2 :TextFieldSprite = t1.clone() as TextFieldSprite;
trace(t2.getText());//TextFieldSprite 1
t2.setText( "TextFieldSprite 2" );
trace(t2.getText());//TextFieldSprite 2
addChild( t2 );
t2.y = t1.height;

实例化一个显示对象数组,这时候便不能使用concat或slice复制数组了,须使用for关键字自行实现复制逻辑,示例如下:

var arr1 : Array = [new TextFieldSprite(), new TextFieldSprite()];

var arr2 :Array = new Array(arr1.length);
var n :int = arr1.length;
for(var j:int=0; j<n; j++)
{
	arr2[j] = arr1[j].clone();
	arr2[j].setText( "TextFieldSprite " + j);
}

arr2.concat(arr1).forEach(
	function(item :DisplayObject, index :int=-1, arr :Array = null) :void
	{
		addChild(item);
		item.y = index * item.height;
	}
);

for关键字在这里是可以保证遍历所有数组元素的,可以使用。运行效果图如下:

 

6,使用原型扩展Array

每次在拷贝数组时,均使用for循环复制数组元素是一件很麻烦的事情,如果Array本身具有一个clone方法该有多好。AS3作为一门支持prototype的语言,可以在Array上扩展出clone方法,示例代码如下:

Array.prototype.clone = function() :Array
{
	var r :Array = new Array(this.length);
	for(var j:int; j<this.length; j++)
	{
		r[j] = this[j].clone();
	}

	return r;
};

var arr1 : Array = [new TextFieldSprite(), new TextFieldSprite()];
var arr2 :Array = arr1.clone();

arr2.concat(arr1).forEach(
	function(item :DisplayObject, index :int=-1, arr :Array = null) :void
	{
		addChild(item);
		item.y = index * item.height;
	}
);

 

7,AS3中深拷贝最佳实践

如果你在使用Gumbo进行项目开发,Adobe在mx.utils.ObjectUtil中提供了两个深拷贝对象的方法:

1) copy

该方法使用ByteArray实现,与前面的clone方法的实现原理一致。该方法可用于深拷贝非复合并且非由Flash Player定义的对象。

2) clone

该方法内嵌了copy方法调用,可用于深拷贝copy不支持的复合对象,但也不能拷贝由Flash Player定义的显示对象。这个方法不能在数字for循环中调用,因为它是耗费用户CPU的操作,使用它要注意节约。

如果你不在使用Gumbo开发,按上面的方法实现ICloneable接口,然后使用for循环复制到新数组元素。使用prototype扩展clone方法并非明智之举!

为什么说使用prototype扩展clone方法并非明智之举?

8,问题

1)在AS3中有哪些对象作为数组元素时,是引用类型?

2)为什么说for不能保证遍历数组的所有元素?

3)为什么在AS3中不能使用ByteArray拷贝显示对象?使用它拷贝出来的对象为什么不能转换为DisplayObject?

4)为什么说使用prototype扩展clone方法并非明智之举?

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值