ActionScript3 容易中招的地方

其实这一部分想写很久了,一直没好好总结。

在刚开始使用 AS3 的过程中,大部分开发者都会想当然。包括本人,虽然没深入接触其他语言,本人也是从 AS1/2 这样过来的,但 AS3 用久了之后,也发现绝对不能抱着“和以前差不多”的想法。

有相关经验不一定是件好事。它有可能让你能举一反三,也有可能让你走上歪路。

最好办法是在开始学习一门新语言的时先对自己“洗洗脑”。要记住,很多语言类似但是开发平台的特性是差异很大的(AS3 跑的是 AVM2,AS1/2 是 AVM1,实际上已经是两个不同的平台了)。使用 AS3 的过程中要多参考 ActionScript 3.0 开发人员指南

(本文出处:dreamana.com 作者:civet)

显示编程部分

头号杀手:addChild(), removeChild()

普遍被误解成:我要显示一个东西,就 new 一个显示对象,然后 addChild() 就出来了哦;然后要删掉的时候,就 removeChild() 它。(注意,这里是想当然的 删除 哦)

首先我们要明白一点,new 一个对象,这个对象不会马上被回收的,甚至你 removeChild() 了它,这个对象仍然存在与内存的。只有当满足了被回收的条件,GC(Garbage Collection) 才会在下一周期内把它干掉。

第二,addChild() removeChild() 这 2 个只是显示列表操作,表示将某个显示对象加入或移出显示列表,包含在显示列表的显示对象才参加渲染。

从此处就引出更多关于 AS3 程序效率的问题了。

很多人不理解什么不能直接把对象删除,为什么 GC 回收反应那么慢,甚至想手动去强制 GC 执行回收。

既然选择了 ActionScript3,其实就不用花心思去手动管理内存了。这和 Java 类似的,是虚拟机帮助程序员管理的,这样可以预防内存控制方面的错误,降低程序员学习的门槛。

再就是,有了 GC 的自动管理,就不应该去干预它。我们应该做的是养成良好的编程习惯,比如使用 ObjectPool,重用对象等等。因为不论创建一对象还是销毁一个对象本身都是一种消耗,没理由说伯伯种地很消耗体力,阿姨扫地就不费劲吧?

别被忽悠了:Timer 编程

可以这样说,目前 Flash 的循环刷新画面方式都是基于帧的,而非基于时间的。因此是很难做到精确到毫秒的重复执行。

那么 Timer 呢?即使 Timer 的时间间隔是以毫秒为单位,计时器理论上本身是准确的,但是在使用过程中各种因素导致它不能准确。Timer 仍然受到帧速限制的,你不能给 Timer 设置一个超过舞台帧速的时间间隔。而且不管将它的时间间隔设置得多小,都需要等到响应函数全部执行完毕,才可能执行下一次。详细可参阅 Keith 写的一篇 Programming with Timers: Don’t be fooled

注册点,你不用动它

可能因为需要绘画矢量图的关系引入了“注册点”这种概念。有了这样的设定有时候就变得麻烦了,设计师们可能会问你这个那个应该以什么地方作为注册点?(我个人比较喜欢就统一以矩形区域的左上角作为注册点)

为什么要考虑这个问题?因为要旋转?缩放?对齐?

于是网上就出现了所谓的“动态改变注册点”的方法。看起来解决了,用了一些很怪异的方法。但其实,怎么样做,注册点不能改变。

你知道 Matrix 吗?大概,很多人都不知道……

不要以为那些常用的变换,只能通过修改 rotation,scaleX,scaleY 等属性,别忘了还有一个 DisplayObject.transform.matrix

有点别扭的 Loader

除了 MovieClip 对象,另一个比较怪异的存在就是 Loader 了。加载外部图片和 SWF,AS3 重新抽象成 Loader 之后,某些情况却显得不方便。要多留神的有好几个地方:

  1. 事件触发是 LoaderInfo,而不是 Loader 本身:Loader.contentLoaderInfo.addEventListener()

  2. 最好有 IOErrorEvent 的异常处理

  3. 最好不要直接用 Loader 处理鼠标事件,尽量用 content 本身去处理

  4. Loader.unload() 主要作用是解除与子级的关系,相当于 removeChild(Loader.content)。如果加载到的内容是 SWF,不会停止 SWF 的一切运作,需要停的话应该用 unloadAndStop()

用户交互

注意 ROLL_OVER 与 MOUSE_OVER 的差别

写鼠标时间侦听代码的时候,如果前面写了 MouseDown, MouseUp 之类的,可能因为惯性,有时候就将将鼠标悬停事件写成 MouseOver 了。表面看起来能正常运作,但其实 RollOver 与 MouseOver 之间是有区别的!要是碰上了一个复合结构的显示对象就很容易导致 BUG 了。我们要注意看 Flash 是 API 的文档。望文生义也是编程的大忌 :)

该侦听哪个对象的 MOUSE_UP 事件 ?

跟 RollOver 一样,MouseUp 的 BUG 也是很常见。每当看到某些作品在鼠标交互方面有仍这种 BUG 的时候,我感觉这个体验就大打折扣了,严重的还要重新打开。

因为只从到了 AS3 之后,一直以来是没有 ReleaseOutside 事件的(注:Flash Player 11.3 之后重新支持 ReleaseOutside),所以要通过一些技巧才能实现 ReleaseOutside 的效果。比如:MouseDown 之后,侦听 stage 的 MouseUp,而不是目标显示对象的 MouseUp。

网络与通信

Socket 的 SOCKET_DATA 事件的触发条件 ?

Socket 的 SocketData 事件跟 XMLSocket 的 Data 事件是不一样的。XMLSocket 每次触发 Data 事件收到的是一个完整的 XML 文档。

而 Socket 触发 SocketData 的时机是不能预计的,它不会在服务端发送一段完整数据之后才触发(不同于 XMLSocket,XMLSocket 的服务端需要遵守一些协议,其中:“每个 XML 消息都是一个完整的 XML 文档,以一个零 (0) 字节结束。”)。可能在某一次触发,只收到服务端发来的一部分数据,又或者多段数据粘在一起。因此需要自行缓存这些数据,根据需要自行解析数据。

数据结构

Vector 的初始化

指定 Vector 初始值的时候,很容易习惯性写错……

错误的写法:

var v:Vector.<T> = new Vector.<T>([E0, ..., En-1 ,]);//wrong!

正确的写法:

  1. 使用 Vector 字面量语法构造函数

     var v:Vector.<T> = new <T>([E0, ..., En-1 ,]);
    
  2. 使用 Vector.() 全局函数

     var v:Vector.<T> = Vector.<T>([E0, ..., En-1 ,]);