《AS3 Expert》:AS3中的八种基元类型

《AS3 Expert》:AS3中的八种基元类型

2008-05-01  程序员LIYI

,阅读约需 7 分钟

这篇文章属于 AS3 Expert 的一部分,单击链接查看所有内容。

一,八种基元类型 #

何谓基元类型?即是一门语言中最基本基本的数据类型,其它数据类型均是以此为基础定义的,并且可以作为变量注释类型或函数返回类型。在AS3中,共有八种基元类型:

[此有图,但已被岁月冲走]

1)Boolean

布尔类型,仅有true与false两个值。默认值为false。特殊值NaN,undefined,null,字符串空值(”),0转换为Boolean后均为false。

2)Number

默认值为NaN。Number为64位的浮点数值类型,按IEEE-754标准设计。在IEEE-754标准中,一个符号位,11个指数位,52位有效数字位。Number仅有52位有效数位,为什么可以表示最大为253的精度呢?

3)uint

默认值为0。unit表示32位的无符号整数。uint的正数范围比int大,但它在多数场合并不适合代替int。

4)int

默认值也是0。int应该是AS3中使用最广泛的数值类型。

5)String

默认值为null。C#中有一个isNullOrEmpty方法,在AS3中,字符串null与空值,转换为Boolean,均是false。

6)*(任意类型)

它有一个特殊的值,undefined。在变量类型注释或函数返回注释中,使用*表示,它可以是任意子实际类型。

7)Object

在AS3中,一切皆是Object,所有Flash Player定义的,AS3中定义的对象均继承于它。默认值为null。

8)void(无类型)

Adobe官方在《flash as3 programming》中对此类型有如下描述:

void 数据类型仅包含一个值:undefined。

这个描述是不恰切的,undefined作为AS3语言中一个特殊的值,是所有原本要定义却未定义的变量的默认值,而不是void类型的默认值。void作为唯一一个仅能作为函数返回类型注释的基元类型,既然表示‘无’,便应该没有任何值。

void仅能作为函数返回类型注释,这使它位列八种基元类型的理由不是那么充分,但除此之外,我们不知道还能把它归到哪一类别里。

二,Number与int, uint的性能对比 #

我辈非Adobe官方,仅能使用实验的方法。为了测试三个数值类型的运算性能, 作者 设计了这样一个代码实验:

public function IntegerTest() {
	super();

	var t1:Number = new Date().getTime();
	for (var j:int=0; j<100000000; j++) {
		//
	}
	var t2:Number = new Date().getTime();
	trace("Number:", t2 - t1);//1373

	t1 = new Date().getTime();
	for (var k:int=0; k<100000000; k++) {
		//
	}
	t2 = new Date().getTime();
	trace("int:", t2 - t1);//1297

	t1 = new Date().getTime();
	for (var n:uint=0; n<100000000; n++) {
		//
	}
	t2 = new Date().getTime();
	trace("uint:", t2 - t1);//2624
}

这个代码实验,分别使用Number,int,uint进行一亿次的递增与大小判断,实验结果表明,int的效率最高,Number与之相差无几,uint的效率最低,几乎比int、Number慢两倍左右。

但是这个实验的结果是值得怀疑的,因为AS3的编译器有可能对三个作用类似的for作了优化。使用SWFScan扫描swf文件,反编译出来的源码为:

public function IntegerTest() {
	super();
	var loc0:* = getTime();
	var loc1:* = 0;
	while(loc1 < 100000000)
	{
	    loc1 = loc1 + 1;
	}
	var loc2:* = getTime();
	loc0 = getTime();
	var loc3:* = 0;
	while(loc3 < 100000000)
	{
	    loc3 = loc3 + 1;
	}
	loc2 = getTime();
	loc0 = getTime();
	var loc4:* = 0;
	while(loc4 < 100000000)
	{
	    loc4 = loc4 + 1;
	}
	loc2 = getTime();
	return;
}

从反编译出来的源码看,三个for循环的代码是一样的。当然这时候,我们假定反编译工具没有问题。

可能我们需要改进一下这个实验:

var n1 :Number = 0;
var t1:Number = new Date().getTime();
for (var j:int=0; j<100000000; j++)
{
	n1 = j + 1.0 * 10 - 1.0 * 10;
}
var t2:Number = new Date().getTime();
trace(n1, "Number:", t2 - t1);//99999999 Number: 17889

var n2 :int = 0;
t1 = new Date().getTime();
for (var k:int=0; k<100000000; k++)
{
	n2 = k + 1 * 10 - 1 * 10;
}
t2 = new Date().getTime();
trace(n2, "int:", t2 - t1);//99999999 int: 19729

var n3 :uint = 0;
t1 = new Date().getTime();
for (var n:uint=0; n<100000000; n++)
{
	n3 = n + 1 * 10 - 1 * 10;
}
t2 = new Date().getTime();
trace(n3, "uint:", t2 - t1);//99999999 uint: 21455

我修改的依据是:

1)针对同一个问题,用不同方法或原则,实现3个并行方案

2)3个方案的输出结果相同

从实验结果来看,Number的效率最高,int随之,uint最慢。这个结果虽然基于实验,却不能令人信服。

推测:在AS3中,所有内部运算均是以Number进行的,这种推测可以解释上述反编译代码相同的现象。如果推测正确的话,使用不同数值类型的效率差异是由于类型转换造成的,而不是由于本身运算造成的,因运算过程中的类型转换而耗费的CPU应该远高于运算本身,如果Adobe为了避免在运算过程中的频繁类型转换而在运算式内部默认使用Number进行运算也是完全符合逻辑的。

作者之前,已经有不少前辈做过类似的数组类型效率代码实验,有兴趣的朋友可以Google一下,但他们的实验结果却不尽相同。所以,拿Number,int与uint进行效率实验是没有意义的,因为实验本身可能存在问题,或许三条起跑线并不一致,所以也无法评定最终结果。

三,数值类型运用最佳实践 #

1)在for循环中使用int类型

2)定义颜色变量时,使用uint

3)定义枚举变量时,使用uint

4)动态改变显示对象的x,y属性时,使用int

5)在进行复杂的算术运算时,各算术因子均使用Number

6)尽要不用使用Number用在if中作大小,等于判断

四,哪些是值类型,哪些是引用类型? #

在AS3中,虽然所有数据类型均继承于Object,也就是说它们均具有Object拥有的属性和方法,Boolean也不例外,但是除Object以外的所有基元类型均是值类型,当软件工作者创建一个值类型变量时,并未创建一个对象,AS3在内部把它们作为值来对待,这减少了创建对象的开销,这使基元类型的使用效率更高。

即使显式调用值基元类型的构造函数,如new String(“liyi”)、new Number(123),也并未实际创建对象。

所有基元类型均是值类型。Object并不一定是引用类型,近一步判定取决于它的实际数据。Array,XML,XMLList,Function,Event,Error,Class,DisplayObject等均是引用对象。

在AS3中,明确哪些对象是引用类型,具有十分重要的意义,当你开发十万行代码以上的项目时,便会认同我的说法。

五,变量的类型并不取决于变量类型注释 #

在AS3中,变量类型注释是给编译器用的,它并不能决定变量的真实类型。在 这里有位英雄写了这样一则评论:

I've been trying to make an example that displays the difference between a shallow and deep copy. So far I haven't been able to find any difference between using the slice/concat methods and the clone function provided in this section. Can someone take a look at my code and tell me what I'm doing wrong?

//-- CLONING ARRAYS --\\ 
trace("**Cloning Arrays **");
var proto:Object = true;
var original:Array = new Array();

original = [proto];
var shallow:Array = original.slice();
var deep:Array = clone(original);
trace("\tDeep copy = " + deep); // output: true 
trace("\tShallow copy = " + shallow); // output: true 
trace("\n***VALUE CHANGED***");

proto = false; // object value is changed 
trace("\tDeep copy = " + deep); // output: true 
trace("\tShallow copy = " + shallow); // output: true <-- should be false ?? 

// Clone function 
import flash.utils.ByteArray;
function clone(source:Object):* // function for deep copy {
  var myBA:ByteArray = new ByteArray();
  myBA.writeObject(source);
  myBA.position = 0;
  return(myBA.readObject());
}
maybe I'm just confused as to what deep and shallow copies are any explanations would be great thanks.

他使用Adobe提供的ByteArray clone方法对数组进行深拷贝,当他改变proto的值为false时,却发现shallow[0]并没有改变。在这里,proto的变量注释虽为Object,但它其实并不是Object,而是Boolean,proto的真实类型取决于它真实的值,由于他以布尔值true实例化proto,所以proto论为了Boolean类型。在AS3中,Boolean并非引用类型。

把这位英雄的代码稍作两处改动,如下:

//-- CLONING ARRAYS --\\
trace("**Cloning Arrays **");
var proto: Object = [true];
var original:Array = new Array();
original = [proto];
var shallow:Array = original.slice();
var deep:Array = clone(original);

trace("\tDeep copy = " + deep); // output: true
trace("\tShallow copy = " + shallow); // output: true

trace("\n***VALUE CHANGED***");
proto[0] = false; // object value is changed

trace("\tDeep copy = " + deep); // output: true
trace("\tShallow copy = " + shallow); // output: false

这样由于Array是引用类型,所以输出便如期许了。

六,问题 #

1)Number仅有52位有效数位,为什么可以表示最大为253的精度呢?

2)值基元类型如Number,String等,既然是值,为什么又有Object的方法呢?在AVM内部是如何实现的?

3)为什么定义颜色值使用uint,而不使用int或Number?

4)为什么不能使用Number动态改变显示对象的坐标?

2008年5月

版权声明

与友分享

网站访问量(PV): ,你是第 位访客(UV)