AS3 面相對象 高級話題

官方幫助上的一段文章,大致瞭解了下as3的一些機制.由於各方面水平都比較菜,所以翻譯出來的,可能比一直被我鄙視的"國人翻譯的東西"還要差.不過我已盡力而爲了,希望看的朋友包含,如果有錯誤歡迎指出.如果要轉載,當然受寵若驚,但請加上出處.謝謝!

原文地址:http://livedocs.macromedia.com/flex/2/docs/00001847.html

===================================================================

這部分以簡短概述ActionScript的OOP歷史爲開始,接着討論ActionScript3.0的對象模型,以及它是如何讓新的AVM2(ActionScript虛擬機2)比起以往FlashPlayer所包含的AVM1,在執行效率上有了明顯的提升.

小標題
ActionScript中的OOP歷史
ActionScript 3.0的類對象(class object)
特性對象(The traits object)
原型對象(The prototype object)
命名空間AS3(The AS3 namespace)


ActionScript中的OOP歷史
因爲ActionScript 3.0構建於先前版本的ActionScript之上,所以瞭解ActionScript對象模型的發展,可能會有所幫助.
ActionScript一開始是以一種簡單的腳本機制來作爲早期Flash的創造工具.漸漸地,程序員們開始用它來創建複雜的程序.爲了響應程序員的一些需求,在每次新的發佈中都對其語言性能有一定的增強,爲的就是方便複雜程序的創建.

ActionScript 1.0
ActionScript 1.0是Flash Player6或更早的版本中所用的語言.在如此早期的開發過程中,ActionScript對象模型是基於基本數據類型概念上的.一個ActionScript對象就是一個混合了一組屬性(property)的數據類型.在我們討論對象模型時,屬性這個術語,包括一個對象內的所有內容,如變量,函數.

雖然,第一代ActionScript不支持用class關鍵字來定義類,但仍然可以用一個特殊的對象來定義,這個對象叫做原型(prototype)對象.它把本來應該使用關鍵字class定義的抽象數據類型,定義在一個具體的對象內.就像在JAVA和C++這種基於class的語言裏所做的那樣,基於原型的ActionScript 1.0,使用一個存在的對象作爲其它對象的模型(或原型).在基於class的語言中,一個類的作用相當於該類對象的模板,而在基於原型的語言中,對象的模板是另一個對象的原型.

在ActionScript 1.0中創建一個類,需要定義一個構造函數.而函數確是一個實際的對象,並不是一個抽象的概念.創建的構造函數相當於該類實例的原型所屬的對象.下面代碼創建了一個名爲Shape的類,定義了一個屬性visible,並初始爲true:

// 基類
function Shape() {
}
// 創建一個名爲visible的屬性
Shape.prototype.visible = true;

構造函數定義了一個Shape類,可以用new操作符來實例化,如下:

myShape = new Shape();

構造函數Shape是一個對象,作爲Shape類的實例的原型,也能作爲Shape類的子類的原型.

創建一個Shape類的子類有兩個過程.
首先,創建子類的構造函數,如下:

// 子類
function Circle(id, radius) {
    this.id = id;
    this.radius = radius;
}

其次,使用new操作符來聲明Shape類是Circle類的原型.默認情況下,所有創建的類都使用Object類作爲其原型,意思就是說,當前Circle.prototype包含了一個一般的對象(Object類的實例).爲了讓Shape成爲Circle的原型,使用以下代碼改變Circle.prototype,讓其包含一個Shape對象.

// 讓Circle成爲Shape的子類
Circle.prototype = new Shape();

現在,Shape類和Circle類以一種繼承關係,也就是所謂的原型鏈(prototype chain)連在了一起,下圖描繪出了這種原型鏈關係:



每個原型鏈的基類都是Object類.Object類包含了一個靜態屬性,叫做Object.prototype,它被指定成爲所有被創建對象的原型.在原型鏈中的第二個對象是Shape.因爲Shape.prototype屬性沒有被明確設置,所以缺省保留的是一個一般對象(Object類的實例).原型鏈的末端是Circle類,它的原型被連接到了Shape類(Circlr.prototype持有一個Shape對象).

如果創建一個Circle類的實例,實例將繼承自Circle類的原型鏈:

// 創建一個Circle類的實例
myCircle = new Circle();

重新調用先前創建的屬性visible.在該例中,visible屬性不是myCircle對象的一部分,而是Shape對象的一個成員,但仍然輸出爲true:

trace(myCircle.visible); // 輸出: true

Flash Player爲了確定myCircle繼承了visible屬性,於是走了一遍原型鏈.當執行trace代碼時,Flash Player首先搜索整個myCircle的屬性,找有沒有一個叫visible的,可是沒有找到.Flash Player繼續在Circle.prototype中找,但仍沒找到.不斷沿着原型鏈向上,終於在Shape.prototype中找到了visible屬性,於是輸出該屬性的值.

這個簡單而有趣的例子,忽略了大多細節和複雜的原型鏈,目的是爲了提供足夠的信息幫助你瞭解ActionScript 3.0 對象模型.

ActionScript 2.0
ActionScript 2.0引進了新的關鍵字,class,extends,public和private.這些可以讓類的定義近似於JAVA和C++這種語言.但重要的是要知道,ActionScrtip 2.0和ActionScrtip 1.0比起來,底層繼承機制並沒有任何改變.ActionScrtip 2.0僅僅是增加了一個新的語法來定義類罷了.工作方式還是一如既往的原型鏈.

下例中介紹了ActionScript 2.0的新語法,使得定義一個類,變的非常直觀:

// 基類
class Shape {
    var visible:Boolean = true;
}

注意ActionScript 2.0還引進了編譯期類型檢測.上述的聲明,使得visible屬性只能包含一個Boolean值.新的關鍵字extends,使創建子類的過程得到了簡化.在ActionScript 1.0中需要兩步的,只需要一步了:

// 子類
class Circle extends Shape {
    var id:Number;
    var radius:Number;
    function Circle(id, radius) {
        this.id = id;
        this.radius = radius;
    }
}

現在,構造函數成爲類定義的一個部分,而屬性id和radius必須被明確聲明.

ActionScript 2.0還支持接口的定義.這點增強了面向對象編程,使內部對象通訊有了正式協議的定義,

ActionScript 3.0的類對象
一般的面相對象編程範例(paradigm) - 大多想到JAVA和C++ - 是使用各種類來定義對象的類型.
程序設計語言採用該範例,用類來構造實例的數據類型,這就是類的定義.
ActionScript使用的類也是爲了這兩個意圖,但根本上,是作爲一個基於原型的語言增加了這麼一個特性.ActionScript爲每個類的定義創建了一個特殊對象,來共享其行爲和狀態.大多數ActionScript程序員,是不會在實際編程中牽涉到這個特性.ActionScript 3.0在設計上,使你在以面相對象編程時,不會用到甚至也不用着瞭解這些特殊的類對象.這部分是爲了給想利用這種特殊類對象的高級程序員作得深層次討論.

下圖顯示了一個類對象的結構,由語句class A{};定義的一個簡單的類A:


圖中每一個矩形表示一個對象.對象內下腳的字母A,表示其屬於類A.類對象(CA)包含了一系列其它重要對象的引用.特性對象(TA)(traits object)保存了定義在類中實例的屬性.類特性對象(TCA)表示了類的內在類型,且以靜態屬性的方式保存在類中(下腳的字母C,表示"類").原型對象(PA)總是引用類對象一開始就有的constructor屬性.

特性對象
在ActionScript 3.0中,特性對象作爲新內容,目的是爲了提升執行效率.在以往版本的ActionScript裏,沿着原型鏈搜索變量名,是一個很耗時的過程.在ActionScript 3.0中,名稱的搜索效率有着顯著的提高,因爲從父類繼承下來的屬性被複制到了子類的特性對象內.

程序員無法從代碼中直接訪問特性對象,但是能很明顯的感覺到,它的存在改進了效率和內存的使用.特性對象爲AVM2提供了關於類的設計和內容方面的詳細信息.根據這些信息,AVM2可以大大減少執行時間,因爲它常常能直接產生機器指令來訪問屬性或者調用函數,而無需耗時的去搞名稱搜索.

感謝特性對象,讓一個對象的內存佔用遠遠小於了以往版本的ActionScript.舉個例子來說,如果一個類是封閉的(即,類沒有被定義成動態dynamic),該類的實例就不需要一個能動態添加屬性的hash表,只需保存一個特性對象的引用和一些在類定義中的固定屬性.結果,一個在ActionScript 2.0裏佔據內存100字節的對象,在ActionScript 3.0裏面只佔用了20字節.

注意: 特性對象是一個內在執行體,不保證在將來的ActionScript版本中,不作變動或者去除.


原型對象
每一個ActionScript類對象都有一個屬性叫prototype,它指向類的原型對象.原型對象是ActionScript作爲基於原型語言遺留下來的.詳細信息,查看ActionScript 1.0.

prototype屬性是隻讀的,不能修改其指向其它對象.這點和以往的ActionScript不一樣.雖然prototype屬性是隻讀的,但它指向的原型對象不是.換句話說,可以爲原型對象增加新的屬性.增加的新屬性可以被所有該類的實例共享.

原型鏈是以往的ActionScript中唯一的繼承機制.在ActionScript 3.0中它只是一個次要角色.主要的繼承機制,固定屬性繼承,已由特性對象在內部處理了.
固定屬性就是指在類中定義一個變量或函數,定義的變量或函數則作爲類定義的一個部分.
固定屬性繼承也叫做類繼承,和該繼承機制相關聯的關鍵字有,如class,extends,override.

原型鏈提供的是另一種繼承機制,比起固定屬性繼承更具動態性.你可以向一個類的原型對象中添加任何屬性,該屬性不一定是類定義的一部分,可以是在運行期內通過類對象的prototype屬性添加的.注意,當設置編譯器爲嚴格模式時,是無法對添加在原型對象上的屬性進行訪問的,除非用dynamic聲明該類是動態的.

一個很好的例子就是,Object類的原型對象上添加的幾個屬性.toString()和valueOf()函數,就是被分配在Object類的原型對象上的.下面這個例子,從理論上講了如何聲明這兩個函數(執行起來因具體情況不同而有所差異)

public dynamic class Object {
    prototype.toString = function () : String {
        // 語句
    }
    prototype.valueOf = function () {
        // 語句
    }
}

正如之前提到的,還可以從外部向類的原型對象添加屬性:

Object.prototype.toString = function () : String {
    // 語句
}

在重新定義子類函數時,原型繼承不像固定屬性繼承那樣,需要override關鍵字.比如,如果要重新定義Object子類中的valueOf()函數,有三種方法.
其一,在子類的定義內,於其原型對象上定義一個valueOf()函數.以下代碼創建了一個Object的子類叫Foo,且在Foo類的定義內,對其原型對象上的valueOf()函數進行了重新定義.因爲每個類都繼承自Object,所以extends可以省略.

dynamic class Foo{
    prototype.valueOf = function() {
        return "Instance of Foo";
    }
}

其二,在外部重新定義,如下:

Foo.prototype.valueOf = function() {
        return "Instance of Foo";
}

其三,在Foo類的定義內,定義一個名爲valueOf()的固定屬性.這個技術不同於其它兩個,它混用了固定屬性繼承和原型繼承.任何一個Foo的子類要想重定義valueOf(),都必須使用override關鍵字,以下代碼顯示了,在Foo中定義的一個valueOf()固定屬性:

class Foo {
    function valueOf() {
        return "Instance of Foo";
    }
}

命名空間AS3

在有着兩種不同的繼承機制下 - 固定屬性繼承和原型繼承 - 要解決有關核心類的屬性/函數的兼容性,是一個有趣的挑戰.

一方面要兼容ECMAScript 4所要求的原型繼承,即在一個核心類的原型對象上定義的屬性/函數,另一方面,還要兼容Flash Player API所使用的固定屬性繼承,即在類定義中使用const,var,function關鍵字定義的屬性/函數.此外,在使用上,要讓固定屬性比原型的執行效率有顯著提升.

ActionScript 3.0通過爲核心類使用原型繼承和固定屬性繼承,解決了上述問題.每一個核心類包含了對屬性/函數的兩種設置.一種設置,就是兼容ECMAScript所要求的通過原型定義;另一種設置,是兼容Flash Player API的由固定屬性和命名空間AS3來定義.

命名空間AS3在兩種設置的選擇上,提供了一個便利機制.如果不使用命名空間AS3,一個核心類實例的屬性/函數,將繼承自該核心類原型對象上所定義的內容.反之,如果使用命名空間AS3,則一個核心類實例的屬性/函數,將來自AS3語言.因爲固定屬性總是優先於原型屬性.換句話說,只要一個固定屬性存在,使用時就將一直替代和該屬性同名的原型屬性.

你可以通過修飾符來使用命名空間AS3中的屬性/函數.下面這個例子,介紹瞭如何使用AS3的Array.pop()函數:

var nums:Array = new Array(1, 2, 3);
nums.AS3::pop();
trace(nums); // 輸出: 1,2

也可以利用use namespace在代碼中直接打開命名空間AS3.下面這個例子,介紹了利用use namespace直接打開命名空間AS3來使用pop()和push()函數:

use namespace AS3;

var nums:Array = new Array(1, 2, 3);
nums.pop();
nums.push(5);
trace(nums) // 輸出: 1,2,5

ActionScript 3.0的編輯器提供了一些選項,用來設置整個程序是否應用命名空間AS3.編譯選項-as3指AS3命名空間,選項-es指原型繼承(es爲ECMAScript).要打開命名空間AS3,就把-as3設置爲true,-es設置爲false.反之亦然.Adobe Flex Builder 2的默認設置爲 -as3=true -es=false

如果想擴展任何核心類,重載其任何函數.你應該瞭解了命名空間AS3所帶來的影響,且如何去聲明一個重載函數.如果是使用命名空間AS3,那麼重載任何一個核心類的函數就必須使用命名空間AS3,並且要標有override.如果不是,則用不着使用命名空間AS3和override關鍵字.
發佈了43 篇原創文章 · 獲贊 0 · 訪問量 11萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章