skip to main |skip to sidebar

2008-06-22

[AS3]详解E4X的变量展开(3)——效率与实用性

从上一篇文章可以看出使用 E4X 变量展开要慢于 new String。因为 E4X 是先把字符串转换为 XML(XMLList) 再转换为字符串。在解析字符串时开销很大。下面是测试的结果:

方式
第1次
第2次
第3次
第4次
第5次
平均
E4X 变量展开
1008
1490
959
1037
959
1091
new String
135
110
125
111
117
120

(E4X 变量展开的代码为:<>abc{1+2}def</>.toString(),new String 的代码为:"abc" + (1+2) + "def";循环次数为10000次;单位:ms。)

由此可见 E4X 变量展开的效率要差一些,但不在大量的循环里的时候差别不是很明显。因为这里的差别是循环 10000 次的结果,执行一次的差别仅仅为 0.097(ms),是可以忽略不计的。因此 E4X 变量展开还是很有用的。

比如说以下代码(预载入文本):

E4X 变量展开的格式:

e4xStr(<>Loading... {loaded}K/{total}K ({loaded/total*100}%)</>)
//e4xStr 为
function e4xStr(e4x:XMLList):String {
  return e4x.toString();
}

new String 的格式:

"Loading... " + loaded + "K/" + total + "K (" + (loaded/total*100) + "%)"

由此可见 E4X 变量展开的可读性要大大高于new String 的格式。因为预载入文本一般为每一帧执行一次,所以说一般不会遇到效率的瓶颈。

2008-06-15

[AS3]详解E4X的变量展开(2)——内部实现

[AS3]E4X的变量展开详解(1)——基础,下面来研究E4X变量展开的内部实现。

源文件:

var a = <a>abc{123}def</a>;

使用 abcdump 反编译后的结果:

 0        getlocal0
1 pushscope
2 findpropstrict XML
4 getproperty XML
6 pushstring "<a>abc"
8 pushbyte 123
10 esc_xelem
11 add
12 pushstring "def</a>"
14 add
15 construct (1)
17 getglobalscope
18 swap
19 setslot 1
21 returnvoid

new String() 做比较

源文件:

var a = new String("a")

使用 abcdump 反编译后的结果:

 0        getlocal0
1 pushscope
2 findpropstrict String
4 pushstring "a"
6 constructprop String (1)
9 getglobalscope
10 swap
11 setslot 1
13 returnvoid

会发现 E4X 与 new String() 很相似。

先来看压栈的部分

new String() 的为:

4        pushstring          "a"
6 constructprop String (1)

new String() 是先把 "a" 压入堆栈,再调用构造函数。

而 E4X 的为:

 6        pushstring          "<a>abc"
8 pushbyte 123
10 esc_xelem
11 add
12 pushstring "def</a>"
14 add
15 construct (1)

有点复杂,过程为:

1.把 "<a>abc" 压入堆栈(第6行)

"<a>abc"
XML

2.把 123 压入堆栈(第8行)

123
"<a>abc"
XML

3.把 栈顶(这里为 123)转换为 String。(第10行) esc_xelem 相当于 toXMLString(123) *注意 toXMLString 为内部函数,无法调用。

"123"
"<a>abc"
XML

4.把堆栈的栈顶的两项相加。(第11行)

"<a>abc123"
XML

5.把 "def</a>" 压入堆栈(第12行)

"def</a>"
"<a>abc123"
XML

6.把堆栈的栈顶的两项相加。(第14行)

"<a>abc123def</a>"
XML

7.调用构造函数。(第15行)即返回 new XML("<a>abc123def</a>")

总结

<a>abc{123}def</a>;

就相当于

new XML("<a>abc" + toXMLString(123) + "def</a>");

最后,关于AVM2的格式可以参照AVM2 Instructions,不明白的语句可以去查一查。

未完待续,下一篇详解效率与实用性

2008-06-14

[AS3]E4X的变量展开详解(1)——基础

在 E4X 里可以使用 {} 来进行变量展开。比如下以下代码:

var foo:String = "123";
trace(<>abc{foo}def</>.toString());// -> abc123def

{} 里面可以为在执行期才知道的变量:

var foo:String = Math.random().toSTring();
trace(<>abc{foo}def</>.toString());

{} 还可以使用表达式(运算符、调用函数,etc.):

var foo:String = "123";
trace(<>{foo + foo}</>.toString());// -> 123123
//比较复杂一点的
trace(<>{foo += "456", foo+"789"}</>.toString());// -> 123456789
//调用函数
trace(<>{foo.substr(1)}</>.toString());// -> 23

{} 里无法使用语句,但可以使用匿名函数来代替:

var foo:String = "123";
trace(<>{(function(a){return a + a})(foo)}</>.toString());// -> 123123

使用 E4X 会减少一些代码,方便书写,但注意 <></> 的类型为 XMLList

最后给一个方便的函数:

public function e4xstr(e4x:XMLList):String {
  return e4x.toString();
}
//用法
var a:int = 3;
var b:int = 5;
trace(e4xstr(<>a+b={a+b}, a*b={a*b}</>)); // -> a+b=8, a*b=15

未完待续,下一篇详解 E4X 的内部实现

2008-06-07

[AS3]逻辑和(&&)与逻辑或(||)的妙用

使用 &&|| 可以简化代码,提高可读性。比如以下代码:

var foo:Boolean = true;
var bar:Object = {};
trace(foo && bar);  // -> [object Object]
trace(foo || bar);  // -> true

&&|| 的结果是由运算符的左边的项目(这里是 foo )来决定的。简单地说,规则为:

A && B : 如果 Boolean(A) 为 false 则返回 A,否则返回 B。
A || B : 如果 Boolean(A) 为 true 则返回 A,否则返回 B。

所以实际上并没有进行真正数学意义上的逻辑运算,返回值也不一定是 Boolean 值。

Boolean(A) 的转换规则如下:

Undefiend  : false
Null   : false
Boolean  : 与转换前相同
Number   : 0 或 NaN 为 false 替其他的为 true
String   : 空串为 false  其他的为 true
Object   : true

使用逻辑运算符简化代码

使用逻辑运算符可以简化简单的逻辑判断(if)。

//1. 使用 if
if (foo) {
  doSomething();
}
if (!bar) {
  doSomething2();
}

//2. 使用逻辑运算符
foo && doSomething();
bar || doSomething2();

多重 if 的写法:

//1. 使用 if
if (foo) {
  if (bar) {
    sayHello();
  }
}
 
//2. 使用 if 与逻辑运算符
if (foo && bar) {
  sayHello();
}

//3. 只使用逻辑运算符
foo && bar && sayHello();     

虽然 else 也可以实现,但代码有可能变得难一閲读。

&&= 与 ||=

在需要代入结果时,使用 &&=||= 更方便。

//1. 使用 if
if (foo) {
  foo =  "<" + foo + "/>";
}

//2. 使用逻辑运算
foo &&= "<" + foo + "/>";

2008-04-30

我的 Blog 的 Google PR 上升为 7!

在 2008-4 的 Google PR 的更新里我的 Blog 已经上升为7了。

在这里感谢大家对我的支持。

再请大家多多关注一下我的另一个 Blog —— ActionScript Snippets - 一个专门收集 AS 代码的 Blog,它的 PR 也上升为了3。

2008-04-27

[AS3]ActionScript 中数组的访问的 BUG

重要度:(3/5) 这个问题看似简单,但 debug 时却难以发现。

数组访问时如果下标使用了函数并且使用了 +=-=*= 等运算符时需要特别地注意,函数会被调用两次!

比如以下代码:

var i:int = 0;
var foo = function():int {
trace("foo");
return ++i;
}
var arr:Array = [1,2,3];
arr[foo()] += 10;
trace(arr);

将会输出:

foo;
foo;
1,13,3

而正确的输出为:

foo;
1,12,3

这是因为在编译时编译器只做了一下简单的操作:

(a += b) --> (a) = (a) + (b)

以至于将 arr[foo()] += 10; 编译成 arr[foo()] = arr[foo()] + 10;

2008-04-06

[AS3]mxmlc 编译器的 BUG

PS:因为最近很忙,好久没有更新 Blog 了。

最近发现了一个 mxmlc 编译器的 BUG,当含有 -(true ? 1 : 0) 的代码编译会出错。

比如以下代码:

package {
import flash.display.Sprite;
public class TestBUG extends Sprite {

public function TestBUG() {
trace('Test');
var foo:Number = -(true ? 1 : 0);
}
}
}

使用 mxmlc 编译时会弹出错误:

Error: null
java.lang.NullPointerException
  at macromedia.asc.semantics.ConstantEvaluator.evaluate(ConstantEvaluator.java:1168)
  at macromedia.asc.parser.UnaryExpressionNode.evaluate(UnaryExpressionNode.java:33)
  at macromedia.asc.semantics.ConstantEvaluator.evaluate(ConstantEvaluator.java:1805)
......

但把 -(true ? 1 : 0)- 号去掉后编译却可以正常进行,真奇怪!

影响的版本:

  • 2.0
  • 2.0.1
  • 3.0.0