Flash差异规则

发布时间:2015-10-30

  阅读本篇前需要先阅读的教程内容:

  《LayaFlash安装和部署》

  《LayaFlash新建项目》

  《LayaFlash项目编译》

  《LayaFlash宏编译》

  《LayaFlash新增方法》

  LayaFlash引擎基本支持了全部AS3语言的API,但由于AS3与JS语言巨大的差异,仍有不到十条的LayaFlash开发规则需要开发者熟知,按照规则开发项目,或对已有的项目中不匹配的代码进行调整,因此,无论是开发新项目,还是旧项目的转换,都必须熟悉这些开发规则。


1.显示对象的使用规则
1.1 SimpleButton不支持的四个状态
  
和AS3的SimpleButton类不同的是LayaFlash的SimpleButton继承自MovieClip类,不支持动态设置按钮的四个状态外观,包括按钮弹起(upState)、鼠标悬停(overState)、按下(downState)、点击热区(hitState)四个状态。也因此SimpleButton允许通过addChild方法将按钮图片资源等其他显示对象添加到其中显示。教程提供的示例项目利用了这个特性在JS下显示按钮对象。

  示例项目源码下载:layaFlashSimpleButton.zip 

1.2 加载图片二进制数据时Loader不可用
  用到Loader类的loadBytes方法加载图片的二进制数据时,需要用URLLoader类加载,因为在H5中,图片资源都使用img对象表示,而没有二进制数据可用。

2.必须通过宏编译解决差异的API

  有一些API在返回结果上AS与JS无法保持一致,这部分API要想Flash版本与HTML5的版本保持一致,就需要通过宏编译的方式来解决这个差异,下面分别介绍存在差异的API:

2.1 SharedObject方法差异
  AS3中获取一个未被使用过的ShareObject对象,访问它的size属性返回的值为0,LayaFlash中访问size属性返回值为2。

  示例代码:

var share:SharedObject = SharedObject.getLocal("layaFlashShare");
trace("ShareObject size is: " + share.size);

  另外需要注意的是,往LayaFlash的SharedObject对象中存储数据时,数据不能是自定义类的对象,且每次存储必须调用flush方法。

2.2 hasOwnProperty() 方法差异

  hasOwnProperty()方法用于判断字典对象中是否包含有某个属性,由于原生Dictionary编译成h5后会存在内存泄露,进而导致游戏性能下降,为了彻底解决Dictionary带来的性能问题,LayaFlash提供了一个替代类LayaDictionary,并提供了contains()方法替代hasOwnProperty()方法。

         示例代码:

 var data:LayaDictionary = new LayaDictionary(); 
   data.set("a",1);
   data.set("b",2);
   data.set("c","a string value.");
   data.set(4,[]);
   
   var key:Array = []; //使用一个复杂对象作为键名保存数据 
   data.set(key,"any data");
   
   //输出为true
   trace("Is data has aListKey: " + data.contains(key));

(注意:如果代码部分有看不懂的地方,请大家参考LayaDictionary的使用规则LayaFlash新增方法

3.XML使用规则

  由于JS没有原生的XML处理类可用,所以LayaFlash根据AS3处理XML的功能最大程度支持了一个XML类,用于在JS中处理XML数据,为了兼顾性能和功能,做了一些修改,使用时需要注意一些写法上的差别。

  XML示例项目源码下载:layaFlashXML.rar

3.1 定义XML对象
  涉及直接使用尖括号“<>”语法定义子节点
的方式定义XML时,子节点必须闭合,例如:<a />必须写成<a><a/>。以下是示例代码:

/**
 * 定义XML对象
 *
 */
private function layaFlashXMLInit():void
{
    //AS3中XML正常的初始化方式
    /*[IF-FLASH]*/
    var xml:XML = new XML(<a><b c="1" /></a>);
    //JS中定义XML,子节点标签必须闭合,不支持<a><b c="1"/></a>的写法
    //[IF-JS]var xml:XML = new XML(<a><b c="1" ></b></a>);
    var value:int = int(xml.b.@c);
    trace("xml a.b.@c = " + value);
}

3.2 XML节点名的命名限制

  XML的节点名命名不可以使用一些LayaFlash底层已使用过的限定名,这是因为JS本身没有实现过XML的操作功能,我们在JS里所使用的XML对象是由LayaFlash模拟的XML类实现的,因而在使用的过程中会受到这个限制。这些不可用的限定名:name,node。
  在开发中遇到需要定义XML内容的时候,要尽量避免使用这些限定名作为节点名。
3.3 读取XML属性值的注意事项
  从XML中读取到的属性值,如果原计划要读取到的是数值类型,必须做数值类型的强制转换操作:

var value:int = int(xml.b.@c);

  这里的类型强转操作是比较容易漏掉的地方,在转换编译现有项目的时候也是修改重点之一。
3.4 XML读取子节点时返回值注意事项

  LayaFlash模拟的XML类在读取一个不存在的子节点时,需要先做非空判断,排除此节点不存在的情况:

/**
 * XML中的child方法测试
 *
 */
private function layaFlashXMLChildTest():void
{
    var xml:XML = <a><b>asdfa</b></a>;var v:* = xml.child("c").children();
    trace("v is XMLList: " + (v is XMLList));
    trace(v);
}

  以上示例代码在LayaFlash中的输出内容为:

8.png

  如果尝试获取一个不存在的节点,AS3里返回一个不含子节点的空XMLList对象,JS会返回以一个描述此XML节点的数组内容。此外如果尝试从XML中获取一个不存在的节点:

var xml:XML = <a><b>asdfa</b></a>;
trace("Get a null node: " + xml.a.b.c + ", 
the null node is XMLList: " + (xml.a.b.c is XMLList));

  AS3中同样返回一个不含子节点的空XMLList对象,但在LayaFlash编译后的JS会因为获取一个空对象而报错。因而必须在LayaFlash编译前修改AS3代码,使用一些有可能为空的节点下的内容前添请加非空判断,通过判断以后才在逻辑中使用节点下的内容。

  使用判断表到时从XML中取值时,暂时不支持使用“==”(等于)判断之外的表达式:

var xml:XML = 
            <data>
                <item v="1">layaFlash</item>
                <item v="2">layabox</item>
            </data>;
trace(xml.data.(v == "1"));


4.LayaFlash语法变动
4.1 函数的列表参数...rest
  使用任意个数参数的方式定义的函数,须在参数后加上数组类型的声明:

private function layaResetArgsTest(...args:Array):void
{
    //code here.
}

4.2 for...each
  使用“for...each”时,循环变量不能是类属性,必须用局部变量,否则编译后JS版本获取不到值。在采用LayaFlash开发时,建议不要采用for...each,使用for ...in替代。:

private var _layaForInKey:*; //作为 for each循环时的循环变量的类属性

/**
 * for each 语法差异测试
 *
 */
private function layaForEachTest():void
{
    var list:Array = [1, 2, 3, 4, {"value": "i'm ok."}];
    for each (_layaForInKey in list)
    {
        trace("one item of list: " + _layaForInKey);
    }
}

  AS3的输出结果为:

one item of list: 1
one item of list: 2
one item of list: 3
one item of list: 4
one item of list: [object Object]

  而LayaFlash中的输出结果却是:

7.png

  所有的输出值都为空。将循环变量改用局部变量可解决该差异问题。

4.3.Number类型与整数类型变量

4.3.1.整型变量赋值

  AS3代码里将一个Number类型的值赋给一个整数类型(int、uint)的变量,需要手动做取整操作,因为编译为JS代码后,JS不会对变量值自动取整:

  AS3代码示例:

var numb:Number = 1.6;
var ab:int = numb;
trace(ab);

  兼容JS需要修改为以下代码:

var numb:Number = 1.6;
var ab:int = int(numb);
trace(ab);

  在向函数或方法传递参数时也要做类似的处理。

  AS3代码示例:

var num:int = 2340923944589;
transfrom(num);
public function transfrom(value:uint):void
{
    trace(value);
}

  兼容JS需要修改为以下代码:

var num:int = 2340923944589;
transfrom(num);
public function transfrom(value:uint):void
{
    trace(uint(value));
}

4.3.2.字符串强制取整操作的差异

  使用下列语句对一个字符串取整是,在AS3中得到0,JS中的结果则是“123”:

int("123_word")

4.4.for循环必须使用完整的大括号“{}”

  在书写for循环时,LayaFlash不支持使用单行的循环体写法。LayaFlash编译过程中如果碰到这样的写法,会直接在代码附近添加一些错误标识,破坏代码原貌,提示错误位置。

  以下是一个不支持的for循环写法示例:

var item:*;
var list:Array = ["lucy","mike",2,3];
for each(var item in list)
    trace("item = " + item);

  正确的写法示例,要补完大括号“{}”:

var item:*;
var list:Array = ["lucy","mike",2,3];
for each(var item in list) {
    trace("item = " + item);
}

4.5.使用不同修饰符定义的同名方法会根据创建时序进行覆盖

  由于JS的语法中,所有的方法被定义时都是公开的(public),因而从AS3编译成JS后,如果原来存在两个方法名相同,但使用不同的修饰符(public、private、protected等)定义的方法,在JS中后定义的方法会覆盖早些定义的方法。例如以下代码示例,两个存在继承关系的类的写法在AS3中不会有问题。

  父类代码:

package
{
	public class Base
	{
		public function Base()
		{
			reset();
		}
		
		protected function reset():void
		{
			trace("AS3 for ever.");
		}
	}
}

  子类定义了一个新的reset方法:

package
{
	public class Child extends Base
	{
		public function Base()
		{
			reset();
		}
		
		public function reset():void
		{
			trace("Hi,LayaFlash.");
		}
	}
}

  但在JS中,由于子类的reset方法是在“父类”的reset方法之后被定义,于是父类中的reset方法就被完全覆盖成了子类的方法。解决这个问题的方式是,按照正常的覆盖父类方法的方式定义reset方法,将子类作如下修改即可:

package
{
	public class Child extends Base
	{
		public function Base()
		{
			reset();
		}
		
		override protected function reset():void
		{
		    super.reset();
			trace("Hi,LayaFlash.");
		}
	}
}


5.构造函数注意事项
  书写构造函数的时候,所有修改父类属性的代码必须写在父类构造函数super()的调用语句之后,否则修改属性的代码无效。以下是代码示例:

  作为父类A.as的代码:

package 
{
    import flash.display.Sprite;
    
    public class A extends Sprite
    {
        protected var data:String = "layabox";
        public function A()
        {
            super();
        }
    }
}

   作为子类B.as的代码:

package 
{
    public class B extends A
    {
        public function B()
        {
            this.data = "layaFlash";//这里的修改无效
            trace("data is " + this.data);
            
            super();
            this.data = "layaFlash";//有效的修改属性代码
            trace("data is " + this.data);
        }
    }
}


6.Embed元标签

  LayaFlash支持部分Embed标签,目前支持以下两种:

[Embed(source="xxx.png")]
private var TestImage:Class;

[Embed(source="comp.swf",symbol="xxx")]
private var TestSWF:Class;