AS3中的八种基元类型
一,八种基元类型
何谓基元类型?即是一门语言中最基本基本的数据类型,其它数据类型均是以此为基础定义的,并且可以作为变量注释类型或函数返回类型。在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官方,仅能使用实验的方法。为了测试三个数值类型的运算性能,sban 设计了这样一个代码实验:
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进行运算也是完全符合逻辑的。
在sban之前,已经有不少前辈做过类似的数组类型效率代码实验,有兴趣的朋友可以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("sban")、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动态改变显示对象的坐标?