本文摘译自Sean Moore撰写的Round up of ActionScript 3.0 and Flex optimization techniques and practices ,作者Sean本来列举了51个最佳化AS3的方式,后来依据读者的回应编辑成37个小技巧。 雖然其中有些是ActionScrip程式設計師普遍知道的技巧,但整體而言,仍具有參考價值。虽然其中有些是ActionScrip程式设计师普遍知道的技巧,但整体而言,仍具有参考价值。
在翻译过程中,我把原文底下值得参考的读者回应加到「译注」部分,同时也把刊载于Open Source Flash网站上的AS3 Speed tests当中的要点纳入相关的「译注」。 此外,我把原文的NOT(不要)翻譯成「避免」,因為原本的程式寫法並沒有錯,只是在需要榨乾CPU運算效能的情況下,建議用另一種寫法。此外,我把原文的NOT(不要)翻译成「避免」,因为原本的程式写法并没有错,只是在需要榨干CPU运算效能的情况下,建议用另一种写法
1.建立阵列(Array)时,避免使用new运算子: var a = []; var a = [];
避免寫成:避免写成:
var a = new Array(); var a = new Array();
2.建立陣列會耗費比較多的時間,請謹慎使用: 2.建立阵列会耗费比较多的时间,请谨慎使用:
var vanityCollection01 : Array = new Array(); var vanityCollection01 : Array = new Array();
var vanityCollection02 : Array = new Array(); var vanityCollection02 : Array = new Array();
var vanityCollection03 : Array = new Array(); var vanityCollection03 : Array = new Array();
var vanityCollection04 : Array = new Array(); var vanityCollection04 : Array = new Array();
3.最快速的陣列複製方式(使用concat()方法): 3.最快速的阵列复制方式(使用concat()方法):
var copy : Array = sourceArray.concat(); var copy : Array = sourceArray.concat();
4.設定單一陣列元素值的執行速度比較慢: 4.设定单一阵列元素值的执行速度比较慢:
employees.push( employee ); employees.push( employee );
employees[2] = employee; employees[2] = employee;
5.讀取陣列元素值的速度是設定其值的兩倍: 5.读取阵列元素值的速度是设定其值的两倍:
var employee : Employee = employees[2]; var employee : Employee = employees[2];
譯註:Vector 結構類型(另一種形式的「陣列」),比Array 效率高。 译注:Vector结构类型(另一种形式的「阵列」),比Array效率高。 請參閱ActionScript 3.0程式最佳化(一)裡的「 Vector資料類型簡介 」一節說明。请参阅ActionScript 3.0程式最佳化(一)里的「 Vector资料类型简介 」一节说明。
6.替無須建立物件實體的類別,宣告static(靜態)成員: 6.替无须建立物件实体的类别,宣告static(静态)成员:
StringUtils.trim( 'text with space at end ' ); StringUtils.trim( 'text with space at end ' );
類別定義:类别定义:
package package
{ {
public final class StringUtils public final class StringUtils
{ {
public static function trim( s : String ) : String public static function trim( s : String ) : String
{ {
var trimmed : String; var trimmed : String;
// 方法的程式碼 // 方法的程式码
return trimmed; return trimmed;
} }
} }
} }
譯註:執行static 方法比一般物件的方法更耗費時間。 译注:执行static方法比一般物件的方法更耗费时间。
7.在應用程式的執行週期中不會改變的內容,應使用const 將它宣告為常數: 7.在应用程式的执行周期中不会改变的内容,应使用const将它宣告为常数:
public const APPLICATION_PUBLISHER : String = 'Company, Inc.'; public const APPLICATION_PUBLISHER : String = 'Company, Inc.';
8.不會被繼承的類別(亦即,其下沒有子類別),應該用final 關鍵字宣告: 8.不会被继承的类别(亦即,其下没有子类别),应该用final关键字宣告:
public final class StringUtils public final class StringUtils
9.方法與變數的名稱長度,不影響ActionScript 3.0的效能(其他程式語言也一樣): 9.方法与变数的名称长度,不影响ActionScript 3.0的效能(其他程式语言也一样):
someCrazyLongMethodNameDoesntReallyImpactPerformanceTooMuch(); someCrazyLongMethodNameDoesntReallyImpactPerformanceTooMuch();
譯註:有人持相反的看法,認為過長的變數名稱會影響處理效率。 译注:有人持相反的看法,认为过长的变数名称会影响处理效率。
10.把多個指派設定敘述寫成一行,不會增加效能(其他程式語言也一樣): 10.把多个指派设定叙述写成一行,不会增加效能(其他程式语言也一样):
var i=0; j=10; k=200; var i=0; j=10; k=200;
11. if 條件式和switch 條件敘述的記憶體用量相同: 11. if条件式和switch条件叙述的记忆体用量相同:
if ( 條件式) if ( 条件式)
{ {
// 處理條件 // 处理条件
} }
記憶體用量等同於:记忆体用量等同于:
switch ( 條件式) switch ( 条件式)
{ {
case 'A': case 'A':
// 處理條件A 的邏輯 // 处理条件A 的逻辑
break; break;
case 'B': case 'B':
// 處理條件B 的邏輯 // 处理条件B 的逻辑
break; break;
} }
譯註: if 條件式的執行效率優於switch。 译注: if条件式的执行效率优于switch。
12.把if 條件判斷式中,依照最有可能成立(true)的判斷結果依序安排: 12.把if条件判断式中,依照最有可能成立(true)的判断结果依序安排:
if ( 經常發生的情況) if ( 经常发生的情况)
{ {
// 處理經常發生的程式邏輯 // 处理经常发生的程式逻辑
} }
else if ( 偶爾發生的情況) else if ( 偶尔发生的情况)
{ {
// 處理偶爾發生的程式邏輯 // 处理偶尔发生的程式逻辑
} }
else else
{ {
// 處理較不常發生的程式邏輯 // 处理较不常发生的程式逻辑
} }
13. ActionScript虛擬機器(簡稱AVM)會在迴圈中,把int(整數)類型轉型成Number(數字)類型(Flash Player裡的AVM會隨著版本更新而改進,在第10版中,執行int, uint 和Number 轉換時,不會像以前那樣慢)。 13. ActionScript虚拟机器(简称AVM)会在回圈中,把int(整数)类型转型成Number(数字)类型(Flash Player里的AVM会随着版本更新而改进,在第10版中,执行int , uint 和Number 转换时,不会像以前那样慢)。
14.解決物件類型變換(promotion)、未知類型以及不正確類型的問題。 14.解决物件类型变换(promotion)、未知类型以及不正确类型的问题。
15.審慎使用uint 類型,它的效能也許不佳(Flash Player裡的AVM會隨著版本更新而改進,在第10版中,執行int, uint 和Number 轉換時,不會像以前那樣慢)。 15.审慎使用uint类型,它的效能也许不佳(Flash Player里的AVM会随着版本更新而改进,在第10版中,执行int, uint和Number转换时,不会像以前那样慢)。
var footerHex : uint = 0x00ccff; var footerHex : uint = 0x00ccff;
16.迴圈中的迭代子(iterator,亦即底下程式片段裡的變數i),請宣告成int 類型: 16.回圈中的迭代子(iterator,亦即底下程式片段里的变数i),请宣告成int类型:
for (var i: int = 0; i < n; i++) for (var i: int = 0; i < n; i++)
避免寫成:避免写成:
for (var i: Number = 0; i < n; i++) for (var i: Number = 0; i < n; i++)
譯註1 :while 迴圈的執行效能高於for 迴圈。 译注1 :while回圈的执行效能高于for回圈。
譯註2 :i++ 執行效率遠高於i = i + 1。 译注2 :i++执行效率远高于i = i + 1。 實際上,連續執行四次i++,還比執行一次i = i + 4 快了50%!实际上,连续执行四次i++,还比执行一次i = i + 4快了50%! 此外,a += b 的效率也高於a = a + b。此外,a += b 的效率也高于a = a + b。 然而,減號(-)運算子的效率就沒有明顯的差異,也就是說,i–並不會比i = i - 1還快。然而,减号(-)运算子的效率就没有明显的差异,也就是说,i–并不会比i = i - 1还快。
17.不要在int 類型資料中使用小數: 17.不要在int类型资料中使用小数:
var decimal : Number = 14.654; var decimal : Number = 14.654;
而非:而非:
var decimal : int = 14.654; var decimal : int = 14.654;
譯註1:實際上,int 類型只會保存整數。 译注1:实际上,int类型只会保存整数。
譯註2:取得最小整數、最大整數和四捨五入的Math.floor() 以及Math.round() 靜態方法的執行效率不佳,建議用底下的敘述取代它們: 译注2:取得最小整数、最大整数和四舍五入的Math.floor()以及Math.round()静态方法的执行效率不佳,建议用底下的叙述取代它们:
int(n); // 相當於Math.floor(n); int(n); // 相当于Math.floor(n);
int(n) + 1; // 相當於Math.ceil(n); int(n) + 1; // 相当于Math.ceil(n);
int(n + 0.5); // 相當於Math.round(n); int(n + 0.5); // 相当于Math.round(n);
請注意!请注意! 上面的Math.floor() 與Math.ceil() 的替代語法僅適用於正數 ,底下是負數時的運算情況:上面的Math.floor()与Math.ceil()的替代语法仅适用于正数 ,底下是负数时的运算情况:
var test1:int = int(-1.5); var test1:int = int(-1.5);
//test1 的值為-1 //test1 的值为-1
var test2:int = Math.floor(-1.5); var test2:int = Math.floor(-1.5);
//test2 的值為-2 //test2 的值为-2
取得負值的最小整數和最大整數的解決方案如下(還是比Math 物件的靜態方法快!):取得负值的最小整数和最大整数的解决方案如下(还是比Math 物件的静态方法快!):
var n:Number = -1.5; var n:Number = -1.5;
var test1:int = int(n) + (n >> 0); // 相當於Math.floor(n); var test1:int = int(n) + (n >> 0); // 相当于Math.floor(n);
//test1 的值為-2 //test1 的值为-2
var test2:int = int(n+1) + (n >> 0); // 相當於Math.ceil(n); var test2:int = int(n+1) + (n >> 0); // 相当于Math.ceil(n);
//test2 的值為-1 //test2 的值为-1
譯註3:底下的取絕對值敘述替代方案,會比Math.abs() 快很多: 译注3:底下的取绝对值叙述替代方案,会比Math.abs()快很多:
// 使用Math.abs() 取絕對值 // 使用Math.abs() 取绝对值
var n:Number = -1.5 var n:Number = -1.5
var test:Number = Math.abs(n); var test:Number = Math.abs(n);
// 底下的方案比較快! // 底下的方案比较快!
var n:Number = -1.5; var n:Number = -1.5;
if (n < 0) n = -n; if (n < 0) n = -n;
18.用乘法代替除法,這樣寫不好:5000/1000,乘上小數點比較快:5000*0.001 18.用乘法代替除法,这样写不好:5000/1000,乘上小数点比较快:5000*0.001
譯註:使用位元移位( Bitwise shift)運算子的運算效率更高: 译注:使用位元移位( Bitwise shift)运算子的运算效率更高:
// 使用右移運算子來除以2: // 使用右移运算子来除以2:
trace(10 >> 1); trace(10 >> 1);
// 輸出:5 // 输出:5
// 使用左移運算子來乘以2: // 使用左移运算子来乘以2:
trace(10 << 1); trace(10 << 1);
// 輸出:20 // 输出:20
19.將運算值儲存在變數中,供for 和while 迴圈敘述使用,不要在迴圈中重複計算: 19.将运算值储存在变数中,供for和while回圈叙述使用,不要在回圈中重复计算:
for (..){ a * 180 / Math.PI; } for (..){ a * 180 / Math.PI; }
應該在迴圈外部定義:toRadians = a*180/Math.PI;应该在回圈外部定义:toRadians = a*180/Math.PI;
20.避免在迴圈中進行運算或呼叫方法: 20.避免在回圈中进行运算或呼叫方法:
var len : int = myArray.lengh; var len : int = myArray.lengh;
for (var i=0;i
避免寫成:避免写成:
for (var i=0;i< myArray.lengh;i++){ } for (var i=0;i< myArray.lengh;i++){ }
21.用RegEx(正規表達式)來驗證字串;用字串物件的方法來搜尋文字: 21.用RegEx(正规表达式)来验证字串;用字串物件的方法来搜寻文字:
// 使用正規表達式來驗證美國郵遞區號的例子 // 使用正规表达式来验证美国邮递区号的例子
private var regEx:RegExp = /^[AZ][0-9][AZ] [0-9][AZ][0-9]$/i; private var regEx:RegExp = /^[AZ][0-9][AZ] [0-9][AZ][0-9]$/i;
private function validatePostal( event : Event ) : void private function validatePostal( event : Event ) : void
{ {
if( regEx.test( zipTextInput.text ) ) if( regEx.test( zipTextInput.text ) )
{ {
// 處理格式不符的程式碼 // 处理格式不符的程式码
} }
} }
// 使用字串的方法來搜尋文字 // 使用字串的方法来搜寻文字
var string : String = 'Search me'; var string : String = 'Search me';
var searchIndex : int = string.indexOf( 'me' ); var searchIndex : int = string.indexOf( 'me' );
var search : String = string.substring( searchIndex, searchIndex + 2 ); var search : String = string.substring( searchIndex, searchIndex + 2 );
22.重複使用物件(如:DisplayObject, URLLoader, …),以便維持記憶體的使用效率。 22.重复使用物件(如:DisplayObject, URLLoader, …),以便维持记忆体的使用效率。
23.遵循Flex 的組件模式: 23.遵循Flex的组件模式:
createChildren(); // 建立子物件 createChildren(); // 建立子物件
commitProperties(); // 設定屬性 commitProperties(); // 设定属性
updateDisplayList(); // 更新顯示物件清單 updateDisplayList(); // 更新显示物件清单
24.不得已時才使用Datagrids 組件(先確認你無法用一般的List 組件來完成)。 24.不得已时才使用Datagrids组件(先确认你无法用一般的List组件来完成)。
25.避免在可捲動的資料區域使用Repeater 組件。 25.避免在可卷动的资料区域使用Repeater组件。
26.避免使用setStyle()(Flex框架中,最消耗效能的呼叫之一)。 26.避免使用setStyle()(Flex框架中,最消耗效能的呼叫之一)。
27.使用太多容器(container),將大幅影響應用程式的效能: 27.使用太多容器(container),将大幅影响应用程式的效能:
28.你並不總是需要用容器當作組件的最上層(top-level)標籤,完全合法的組件並不需要最上層容器。 28.你并不总是需要用容器当作组件的最上层(top-level)标签,完全合法的组件并不需要最上层容器。
source="avatar.jpg" width="200" height="200" /> source="avatar.jpg" width="200" height="200" />
29.移除不必要的容器組合,以便減少嵌套的容器。 29.移除不必要的容器组合,以便减少嵌套的容器。
30.避免在標籤元素裡面使用VBox。 30.避免在标签元素里面使用VBox。 (減少贅餘程式) (减少赘余程式)
31.避免在mx:Application 標籤元素中使用VBox。 31.避免在mx:Application标签元素中使用VBox。 (減少贅餘程式) (减少赘余程式)
避免寫成:避免写成:
32.將recycleChildren 屬性設定成true 來改善Repeater 物件的效能(重用之前建立的物件而非重新建立新的)。 32.将recycleChildren属性设定成true来改善Repeater物件的效能(重用之前建立的物件而非重新建立新的)。
[Bindable] [Bindable]
public var repeaterData : Array = ["data 1", "data 2"]; public var repeaterData : Array = ["data 1", "data 2"];
]]> ]]>
33.影格速率(frameRate)設定在60fps 或更低的數值。 33.影格速率(frameRate)设定在60fps或更低的数值。
frameRate="45"> frameRate="45">
34.避免逐格操控多個顯示物件。 34.避免逐格操控多个显示物件。
35. ENTER_FRAME 事件的效率比Timer 事件高。 35. ENTER_FRAME事件的效率比Timer事件高。
public function onEnterFrame( event : Event ) : void public function onEnterFrame( event : Event ) : void
{ {
} }
private function init() : void private function init() : void
{ {
addEventListener( Event.ENTER_FRAME, onEnterFrame ); addEventListener( Event.ENTER_FRAME, onEnterFrame );
} }
避免這樣寫:避免这样写:
public function onTimerTick( event : Event ) : void public function onTimerTick( event : Event ) : void
{ {
} }
private function init() : void private function init() : void
{ {
var timer : Timer = new Timer(); var timer : Timer = new Timer();
timer.start(); timer.start();
timer.addEventListener( TimerEvent.TIMER, onTimerTick ); timer.addEventListener( TimerEvent.TIMER, onTimerTick );
} }
36.要延緩到多個影格之後才建立物件,請使用: 36.要延缓到多个影格之后才建立物件,请使用:
37.若要隱藏物件,請使用visible 屬性: 37.若要隐藏物件,请使用visible属性:
loginButton.visible = false; loginButton.visible = false;
而非:而非:
loginButton.alpha = 0; loginButton.alpha = 0;