图中每一个矩形表示一个对象.对象内下脚的字母A,表示其属于类A.类对象(CA)包含了一系列其它重要对象的引用.特性对象(TA)(traits object)保存了定义在类中实例的属性.类特性对象(TCA)表示了类的内在类型,且以静态属性的方式保存在类中(下脚的字母C,表示"类").原型对象(PA)总是引用类对象一开始就有的constructor属性.
特性对象
在ActionScript 3.0中,特性对象作为新内容,目的是为了提升执行效率.在以往版本的ActionScript里,沿着原型链搜索变量名,是一个很耗时的过程.在ActionScript 3.0中,名称的搜索效率有着显著的提高,因为从父类继承下来的属性被复制到了子类的特性对象内.
程序员无法从代码中直接访问特性对象,但是能很明显的感觉到,它的存在改进了效率和内存的使用.特性对象为AVM2提供了关于类的设计和内容方面的详细信息.根据这些信息,AVM2可以大大减少执行时间,因为它常常能直接产生机器指令来访问属性或者调用函数,而无需耗时的去搞名称搜索.
感谢特性对象,让一个对象的内存占用远远小于了以往版本的ActionScript.举个例子来说,如果一个类是封闭的(即,类没有被定义成动态dynamic),该类的实例就不需要一个能动态添加属性的hash表,只需保存一个特性对象的引用和一些在类定义中的固定属性.结果,一个在ActionScript 2.0里占据内存100字节的对象,在ActionScript 3.0里面只占用了20字节.
注意: 特性对象是一个内在执行体,不保证在将来的ActionScript版本中,不作变动或者去除.
☆
原型(prototype)继承灵巧有余而效率乏力。在AS2/1中,prototype对象并非只读,它充许程序员动态设置prototype对象,这种机制灵活性巨大,但过度灵活方便的prototype却是运行时方法执行效率低下的罪魁祸首,AVM1不得不在变量、方法寻址上浪费大量的时间。
为了解决这个问题,在AS3中,引入了Traits对象,Traits对象在FP内部称为traits_info。每一个类拥有一个traits_info数组,每一个traits_info包括N个Trait对象,Trait是类或对象的固定属性,包括一个名字,一个类型,以及相关数据。
如果有一个Class A继承于Class B,那么A会把B的traits_info复制到自己的traits_info数组中(有一类traits除外,见下)。所有traits_info持有的Trait对象均是引用,所以过度的继承并不会显著增加内存的开销。
在上图中,对于Class A,Ta是其实例变量,实例方法的Traits对象,Tca是其类变量,类方法的Traits对象,它们同处于class_info(FP中Class的内部结构定义名称)中的traits_info数组中,但是Tca在继承时是不会被复制的,所以在子类中无法访问父类的静态变量、方法。
Tca与Ta同处于traits_info中,在名称寻址时,Ta的优先级高于Tca,这意味着,如果在Ca中存在着同名的类变量与实例变量,实例变量将被优先读取。
AS3使用Traits对象实现固定属性继承,相比原来的原型链机制,可在运行时显著减少名称寻址的时间开销。这是AVM2效率提升的最主要原因之一。
PS: 上图中,Class.prototype是类原型,Object.prototype是实例原型,后者是在对象实例化时分配的。
编码规范:尽量不要使用prototype。
原型对象
每一个ActionScript类对象都有一个属性叫prototype,它指向类的原型对象.原型对象是ActionScript作为基于原型语言遗留下来的.详细信息,查看ActionScript 1.0.
prototype属性是只读的,不能修改其指向其它对象.这点和以往的ActionScript不一样.虽然prototype属性是只读的,但它指向的原型对象不是.换句话说,可以为原型对象增加新的属性.增加的新属性可以被所有该类的实例共享.
原型链是以往的ActionScript中唯一的继承机制.在ActionScript 3.0中它只是一个次要角色.主要的继承机制,固定属性继承,已由特性对象在内部处理了.
固定属性就是指在类中定义一个变量或函数,定义的变量或函数则作为类定义的一个部分.
固定属性继承也叫做类继承,和该继承机制相关联的关键字有,如class,extends,override.
原型链提供的是另一种继承机制,比起固定属性继承更具动态性.你可以向一个类的原型对象中添加任何属性,该属性不一定是类定义的一部分,可以是在运行期内通过类对象的prototype属性添加的.注意,当设置编译器为严格模式时,是无法对添加在原型对象上的属性进行访问的,除非用dynamic声明该类是动态的.
一个很好的例子就是,Object类的原型对象上添加的几个属性.toString()和valueOf()函数,就是被分配在Object类的原型对象上的.下面这个例子,从理论上讲了如何声明这两个函数(执行起来因具体情况不同而有所差异)
public dynamic class Object {
prototype.toString = function () : String {
// 语句
}
prototype.valueOf = function () {
// 语句
}
}
正如之前提到的,还可以从外部向类的原型对象添加属性:
Object.prototype.toString = function () : String {
// 语句
}
在重新定义子类函数时,原型继承不像固定属性继承那样,需要override关键字.比如,如果要重新定义Object子类中的valueOf()函数,有三种方法.
其一,在子类的定义内,于其原型对象上定义一个valueOf()函数.以下代码创建了一个Object的子类叫Foo,且在Foo类的定义内,对其原型对象上的valueOf()函数进行了重新定义.因为每个类都继承自Object,所以extends可以省略.
dynamic class Foo{
prototype.valueOf = function() {
return "Instance of Foo";
}
}
其二,在外部重新定义,如下:
Foo.prototype.valueOf = function() {
return "Instance of Foo";
}
其三,在Foo类的定义内,定义一个名为valueOf()的固定属性.这个技术不同于其它两个,它混用了固定属性继承和原型继承.任何一个Foo的子类要想重定义valueOf(),都必须使用override关键字,以下代码显示了,在Foo中定义的一个valueOf()固定属性:
class Foo {
function valueOf() {
return "Instance of Foo";
}
}
命名空间AS3
在有着两种不同的继承机制下 - 固定属性继承和原型继承 - 要解决有关核心类的属性/函数的兼容性,是一个有趣的挑战.
一方面要兼容ECMAScript 4所要求的原型继承,即在一个核心类的原型对象上定义的属性/函数,另一方面,还要兼容Flash Player API所使用的固定属性继承,即在类定义中使用const,var,function关键字定义的属性/函数.此外,在使用上,要让固定属性比原型的执行效率有显著提升.
ActionScript 3.0通过为核心类使用原型继承和固定属性继承,解决了上述问题.每一个核心类包含了对属性/函数的两种设置.一种设置,就是兼容ECMAScript所要求的通过原型定义;另一种设置,是兼容Flash Player API的由固定属性和命名空间AS3来定义.
命名空间AS3在两种设置的选择上,提供了一个便利机制.如果不使用命名空间AS3,一个核心类实例的属性/函数,将继承自该核心类原型对象上所定义的内容.反之,如果使用命名空间AS3,则一个核心类实例的属性/函数,将来自AS3语言.因为固定属性总是优先于原型属性.换句话说,只要一个固定属性存在,使用时就将一直替代和该属性同名的原型属性.
你可以通过修饰符来使用命名空间AS3中的属性/函数.下面这个例子,介绍了如何使用AS3的Array.pop()函数:
var nums:Array = new Array(1, 2, 3);
nums.AS3::pop();
trace(nums); // 输出: 1,2
也可以利用use namespace在代码中直接打开命名空间AS3.下面这个例子,介绍了利用use namespace直接打开命名空间AS3来使用pop()和push()函数:
use namespace AS3;
var nums:Array = new Array(1, 2, 3);
nums.pop();
nums.push(5);
trace(nums) // 输出: 1,2,5
ActionScript 3.0的编辑器提供了一些选项,用来设置整个程序是否应用命名空间AS3.编译选项-as3指AS3命名空间,选项-es指原型继承(es为ECMAScript).要打开命名空间AS3,就把-as3设置为true,-es设置为false.反之亦然.Adobe Flex Builder 2的默认设置为 -as3=true -es=false
如果想扩展任何核心类,重载其任何函数.你应该了解了命名空间AS3所带来的影响,且如何去声明一个重载函数.如果是使用命名空间AS3,那么重载任何一个核心类的函数就必须使用命名空间AS3,并且要标有override.如果不是,则用不着使用命名空间AS3和override关键字.