進階主題

擴充 Array 類別

Array 類別是少數並非 final 的核心類別之一,這表示您可以建立自己的 Array 子類別。本節範例說明如何建立 Array 子類別,並討論幾個可能會在處理過程中產生的問題。

如前所述,ActionScript 中的陣列並非類別陣列,但是您可以建立 Array 子類別,只接受特定資料類別的元素。下一節的範例會定義名為 TypedArray 的 Array 子類別,此一子類別會將其元素限制為第一個參數所指定之資料類型的值。TypedArray 類別只是為了說明如何擴充 Array 類別而提供的範例,基於下列幾個原因,可能不適用於生產目的。第一,類型檢查是在執行階段而非編譯階段發生。第二,當 TypedArray 方法遇到不符的情況時,雖然此方法可輕易加以修改以擲回例外,但是卻不會忽略該情況,而且也不會擲回例外。第三,此類別無法避免使用陣列存取運算子,將任何類型的值插入陣列中。第四,編寫樣式偏重於簡化而非效能最佳化。

宣告子類別

您可以使用 extends 關鍵字,指出某個類別為 Array 的子類別。Array 的子類別應該和 Array 類別一樣都使用 dynamic 特質。否則,您的子類別將無法正常運作。

下列程式碼會顯示 TypedArray 類別的定義,此類別包含一常數,可用來保留資料類型、建構函式方法,以及四種能夠將元素新增至陣列的方法。這個範例雖然省略了每個方法的程式碼,但是在下一節中有充分而詳細的說明:

public dynamic class TypedArray extends Array
{
    private const dataType:Class;

    public function TypedArray(...args) {}
    
    AS3 override function concat(...args):Array {}
    
    AS3 override function push(...args):uint {}
    
    AS3 override function splice(...args) {}
    
    AS3 override function unshift(...args):uint {}
}

這四種覆寫方法都會使用 AS3 命名空間而非 public 特質,因為此範例會假設編譯器選項 -as3 已設定為 true,而且編譯器選項 -es 已設定為 false。這些都是 Adobe Flex Builder 2 和 Adobe Flash CS3 Professional 的預設設定。如需詳細資訊,請參閱AS3 命名空間

提示

 

若您是偏好使用原型繼承的進階開發人員,可以對 TypedArray 類別做兩個小變更,使它在編譯器選項 -es 設定為 true 的情況下進行編譯。首先移除所有的 override 特質,並將 AS3 命名空間更換為 public 特質。其次,以 Array.prototype 代替出現四次的 super

TypedArray 建構函式

子類別建構函式提出相當有趣的挑戰,因為建構函式必須接受任意長度的引數清單。這個挑戰是如何將引數傳遞至父建構函式以建立陣列。如果您將引數清單當做陣列來傳遞,父建構函式會認為它是類型 Array 的單一引數,所產生的陣列永遠只有 1 個元素的長度。處理傳遞引數清單的傳統方式是使用 Function.apply() 方法,這種方法會將引數陣列當做其第二個參數,但是會在執行函數時,將它轉換為引數清單。遺憾的是,Function.apply() 方法不能搭配建構函式使用。

所剩下的唯一選項是在 TypedArray 建構函式中,重新建立 Array 建構函式的邏輯。下列程式碼會顯示 Array 類別建構函式所使用的演算法,您可以將此演算法重新用於 Array 子類別建構函式中:

public dynamic class Array
{
    public function Array(...args)
    {
        var n:uint = args.length
        if (n == 1 && (args[0] is Number))
        {
            var dlen:Number = args[0];
            var ulen:uint = dlen;
            if (ulen != dlen)
            {
                throw new RangeError("Array index is not a 32-bit unsigned integer ("+dlen+")");
            }
            length = ulen;
        }
        else
        {
            length = n;
            for (var i:int=0; i < n; i++)
            {
                this[i] = args[i] 
            }
        }
    }
}

TypedArray 建構函式會共用 Array 建構函式大部分的程式碼,其中只有四個地方不同。首先,參數清單包含新加入之類別為 Class 的必要參數,可讓您指定陣列的資料類型。第二,傳遞至建構函式的資料類型會指定給 dataType 變數。第三,在 else 陳述式中,length 屬性的值是指定在 for 迴圈之後,因此 length 只包含適當類型的引數。第四,for 迴圈的主體會使用 push() 方法的覆寫版本,因此只有正確資料類型的引數才會新增至陣列中。下列範例會顯示 TypedArray 建構函數:

public dynamic class TypedArray extends Array
{
    private var dataType:Class;
    public function TypedArray(typeParam:Class, ...args)
    {
        dataType = typeParam;
        var n:uint = args.length
        if (n == 1 && (args[0] is Number))
        {
            var dlen:Number = args[0];
            var ulen:uint = dlen
            if (ulen != dlen)
            {
                throw new RangeError("Array index is not a 32-bit unsigned integer ("+dlen+")")
            }
            length = ulen;
        }
        else
        {
            for (var i:int=0; i < n; i++)
            {
                // 在 push() 中完成類型檢查 
                this.push(args[i])
            }
            length = this.length;
        }
    }
}

TypedArray 覆寫方法

TypedArray 類別會覆寫 Array 類別的四種方法,而這四種方法都能新增元素到陣列中。在每種情況中,覆寫方法都會新增類型檢查,以防止新增資料類型不正確的元素。接著,每種方法都會呼叫其本身的父類別版本。

push() 方法會以 for..in 迴圈重複執行引數清單,並針對每個引數執行類型檢查。類型不正確的引數會以 splice() 方法從 args 陣列中加以移除。在 for..in 迴圈結束後,args 陣列只會包含類型為 dataType 的值。接著會以更新過的 args 陣列呼叫 push() 的父類別版本,如下列程式碼所示:

    AS3 override function push(...args):uint
    {
        for (var i:* in args)
        {
            if (!(args[i] is dataType))
            {
                args.splice(i,1);
            }
        }
        return (super.push.apply(this, args));
    }

concat() 方法會建立名為 passArgs 的暫時性 TypedArray,以儲存通過類型檢查的引數。這容許重新使用存在於 push() 方法中的類型檢查程式碼。for..in 迴圈會重複執行 args 陣列,並且針對每個引數呼叫 push()。由於 passArgs 的類型是 TypedArray,因此會執行 push() 的 TypedArray 版本。接著 concat() 方法會呼叫本身的父類別版本,如下列程式碼所示:

    AS3 override function concat(...args):Array
    {
        var passArgs:TypedArray = new TypedArray(dataType);
        for (var i:* in args)
        {
            // 在 push() 中完成類型檢查
            passArgs.push(args[i]);
        }
        return (super.concat.apply(this, passArgs));
    }

splice() 方法會採用任意引數清單,但是前兩個引數一定會參考索引編號以及要刪除的元素數目。這就是覆寫的 splice() 方法只會對索引位置 2 或以上的 args 陣列元素進行類型檢查的原因。程式碼中有趣的是,在 for 迴圈中似乎有對 splice() 發出的遞迴呼叫,但是這並非遞迴呼叫,因為 args 的類型屬於 Array 而非 TypedArray,這表示對 args.splice() 的呼叫就是對該方法之父類別版本的呼叫。在 for..in 迴圈結束後,args 陣列在索引陣列位置 2 或以上只會包含類型正確的值,而 splice() 會呼叫本身的父類別版本,如下列程式碼所示:

    AS3 override function splice(...args):*
    {
        if (args.length > 2)
        {
            for (var i:int=2; i< args.length; i++)
            {
                if (!(args[i] is dataType))
                {
                    args.splice(i,1);
                }
            }
        }
        return (super.splice.apply(this, args));
    }

會新增元素至陣列開頭的 unshift() 方法,也接受任意引數清單。遭到覆寫之 unshift() 方法所使用的演算法,和 push() 方法所使用的演算法非常類似,如下列範例程式碼所示:

    AS3 override function unshift(...args):uint
    {
        for (var i:* in args) 
        {
            if (!(args[i] is dataType))
            {
                args.splice(i,1);
            }
        }
        return (super.unshift.apply(this, args));
    }
}

Flash CS3

 

有新的意見加入至這個頁面時,傳送電子郵件給我 | 意見報告

目前頁面: http://livedocs.adobe.com/flash/9.0_tw/main/00000093.html