AS2类初始化成员的Bug

Time:2004-11-23
Environment:Macromedia Flash MX Professional 2004 7.2

在AS2中,允许在声明类成员时同时初始化:
class Cat {
var age:Number = 4; //declaration together with initialization
function Cat() {
}
}
在一般OOP语言中,编译器会根据这种简化语法相应地修改构造函数,完成实例成员的初始化。本来以为AS2亦如此,其实不然。我在写一个歌词解析器的时候因为这个问题郁闷惨了。请看:
//Bug.as Version1
class Bug {
var head:Object = {eyes:2, shape:"round"};
function Bug() {
}
}

//Bug.fla
var bugA:Bug = new Bug();
var bugB:Bug = new Bug();

trace(bugA.head.shape);
trace(bugB.head.shape);

bugA.head.shape = "square";

trace(bugA.head.shape);
trace(bugB.head.shape);

Output显示:
round
round
square
square
改变bugA.head.shape也同时影响了bugB! 从OOP语言的角度讲,这是一个严重的bug!!!难道bugA和bugB的head引用的根本就是同一个对象?
trace(bugA.head == bugB.head); //显示true,果然是这样!
郁闷!这样子根本就没法编程!拿出Debugger,想看一看bugA,bugB的内部情况,结果根本就无法展开.顺便提一句,用AS2的class,都不能展开,很不方便(此说法系谬误,见下文[更正])。

 

这个现象引起了我的回忆:用AS1的时候写类的属性有两种方法,一种是在构造函数种用this.propertyName = value一种是constructorFuncName.prototype.propertyName = value,Moock的ASDG2种两种写法都有提到,但他不赞成后一种。如果用后一种,debugger里是看不到这个属性的。难道AS2将Bug.as Version1翻译成了采用后一种写法的AS1?为了证实这个想法,我写了个AS1的等效类:
_global.Bug = function() {
};
Bug.prototype.head = {eyes:2, shape:"round"};
运行结果与Bug.as Version1完全相同!熟悉AS1 原形拷贝 对象模型的朋友肯定看出原因来了: ) 换一种写法定义类:
_global.Bug = function() {
this.head = {eyes:2, shape:"round"};
};
则运行结果令人满意,debugger中也一目了然:

回到Bug.as Version1:
trace(bugA.head == Bug.prototype.head);//显示true
看来上面的想法的确实对的,AS2编译器处理“声明时就定义”的类成员时,仅仅是简单地将其作为prototype的一个属性,实例化时简单的复制引用(head),而不是在构造函数中初始化,所以bugA.head与bugB.head与Bug.prototype.head指向同一个object。 这算是MM的一个漏洞吧!

重写AS2的Bug类:
//Bug.as Version2
class Bug {
var head:Object;
function Bug() {
head = {eyes:2, shape:"round"}
}
}
一切OK!
总结:由于MM考虑欠周全,在使用“同时声明定义”类成员的简化语法时要小心。只能用于值类型(String, Number, Boolean),若是引用类型,一定要在构造函数种初始化,否则多个实例将共用一个属性,相互影响。

 

[更正]“顺便提一句,用AS2的class,都不能展开,很不方便”

 

这种说法是错的,经研究发现:
凡是在声明时初始化的类实例成员,debugger中都看不到,例如:
//version 1
class Bug {
var legs:Number = 6;
function Bug() {
}
}
在debugger中是看不到legs的,原因是编译器将其翻译成AS1类似于:
_global.Bug = function() {
}
Bug.prototype.legs = 6;
凡是在constructor中初始化的实例成员,debugger中都看得到,例如:
//version 2
class Bug {
var legs:Number;
function Bug() {
legs = 6;
}
}
在debugger中可以看到legs,原因是编译器将其翻译成AS1类似于:
_global.Bug = function() {
this.legs = 6;
}
可以用obj.hasOwnProperty()来验证这一想法:
//新建bug.fla
var bugA:Bug = new Bug();
trace(bugA.hasOwnProperty("legs"));
//使用version 1的Bug.as显示false
//使用version 2的Bug.as显示true

这个现象不能全说是bug,其实是一个缺陷。缺陷的根本原因就是"the compiler’s automatic conversion of ActionScript 2.0 code to ActionScript 1.0 code (which is a necessary evil guaranteeing that most ActionScript 2.0 code will work in Flash Player 6)."
一句话,用AS2写类时最好所有东东都在构造函数里初始化,一方面防止冲突,另一方面也方便调试 : )

Advertisements
This entry was posted in Flash and ActionScript. Bookmark the permalink.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s