命名空間可讓您控制所建立屬性和方法的可見性。請將 public、private、protected 和 internal 存取控制指定字視為內建的命名空間。若這些預先定義的控制指定字不能配合您的需求,您可以自行建立命名空間。
您若熟悉 XML 命名空間,您對此處所列的大部分的討論勢必不陌生,不過,ActionScript 實作的語法和細節都會與 XML 稍有差異;若您從未使用過命名空間,雖然概念本身簡單明瞭,但您還是必須學習有關實作的特定專門用語。
若要瞭解命名空間的運作方式,則知道屬性或方法的名稱永遠包含識別名稱和命名空間兩個部分將會有幫助。識別名稱是您一般所想的名稱。例如,下列類別定義中的識別名稱是 sampleGreeting 和 sampleFunction():
class SampleCode
{
var sampleGreeting:String;
function sampleFunction () {
trace(sampleGreeting + " from sampleFunction()");
}
}
只要定義之前沒有命名空間特質,它們的名稱就會以預設 internal 命名空間加以限定,也就是說,只有在相同套件中的呼叫者才看得見。若編譯器設定為嚴謹模式,編譯器就會發出警告,說明 internal 命名空間會在沒有命名空間特質的情況下套用至任一識別名稱。為確保識別名稱於何處都可供使用,您必須特地在識別名稱之前加上 public 特質。在舊版範例程式碼中,sampleGreeting 和 sampleFunction() 都有 internal 命名空間值。
使用命名空間時,要遵循三個基本步驟。首先,您必須使用 namespace 關鍵字來定義命名空間。例如,下列程式碼會定義 version1 命名空間:
namespace version1;
接著,在屬性或方法宣告中使用命名空間而非存取控制指定字,以套用命名空間。下列範例會將名為 myFunction() 的函數放入 version1 命名空間中:
version1 function myFunction() {}
一旦套用命名空間之後,您就可以使用 use 指令,或是使用命名空間限定識別名稱的名稱來進行參考。下列範例會透過 use 指令,參考 myFunction() 函數:
use namespace version1;
myFunction();
您也可以使用完整名稱來參考 myFunction() 函數,如下列範例所示:
version1::myFunction();
定義命名空間
命名空間包含一個值,也就是「統一資源識別名稱」(URI),它有時候也稱為「命名空間名稱」。URI 可以讓您確保您的命名空間是唯一的。
您將透過其中一種方式來宣告命名空間定義,進而建立命名空間。您可以用明確的 URI 定義命名空間,就像定義 XML 命名空間一樣,或者也可以省略 URI。下列範例將說明如何使用 URI 定義命名空間:
namespace flash_proxy = "http://www.adobe.com/flash/proxy";
URI 是做為該命名空間的唯一識別字串。若省略 URI (如下列範例所示),則編譯器將建立唯一的內部識別字串以取代 URI。您無法存取這個內部識別字串。
namespace flash_proxy;
一旦定義命名空間之後,不管有沒有 URI,命名空間都不能在相同範圍中重新定義。嘗試定義先前在相同範圍中已定義的命名空間會導致編譯器錯誤。
若命名空間是在套件或類別中定義,除非使用了適當的存取控制指定字,否則套件或類別之外的程式碼可能會看不見命名空間。舉例來說,下列程式碼會顯示在 flash.utils 套件中定義的 flash_proxy 命名空間。在下列範例中沒有存取控制指定字,也就是說,只有 flash.utils 套件之內的程式碼才能看見 flash_proxy 命名空間,而在套件之外的任何程式碼都無法看見該命名空間:
package flash.utils
{
namespace flash_proxy;
}
下列程式碼會使用 public 特質,讓套件之外的程式碼可以看見 flash_proxy 命名空間:
package flash.utils
{
public namespace flash_proxy;
}
套用命名空間
套用命名空間就是將定義放入命名空間中。可以放入命名空間中的定義包括函數、變數和常數 (您不能將類別放入自訂命名空間中)。
例如,請考慮使用 public 存取控制命名空間宣告的函數。使用函數定義內的 public 特質將函數放入公用命名空間中,可讓所有程式碼都能使用該函數。一旦定義命名空間之後,使用所定義命名空間的方式與使用 public 特質相同,可參考您自訂命名空間的程式碼都能使用該定義。例如,若您定義命名空間 example1,則可使用 example1 做為特質來加入名為 myFunction() 的方法,如下列範例所示:
namespace example1;
class someClass
{
example1 myFunction() {}
}
使用命名空間 example1 做為特質來宣告 myFunction() 方法,表示此方法屬於 example1 命名空間。
套用命名空間時,應該牢記以下事項:
參考命名空間
使用以任何存取控制命名空間 (例如 public、private、protected 和 internal) 宣告的方法或屬性時,不需要明確地參考命名空間。這是因為這些特殊命名空間的存取權限是由內容所控制。例如,放入 private 命名空間的定義會自動提供給相同類別內的程式碼使用。但是對於您所定義的命名空間,並沒有這種內容感應式功能。若要使用您放入自訂命名空間的方法或屬性,您必須參考該命名空間。
您可以用 use namespace 指令參考命名空間,或透過使用名稱修飾語 (::) 標點符號的命名空間來限定名稱。用 use namespace 指令參考命名空間,會「開啟」命名空間,以便套用至未限定的任何識別名稱。例如,若已定義 example1 命名空間,則可以使用 use namespace example1 存取該命名空間中的名稱:
use namespace example1;
myFunction();
您一次可以開啟多個命名空間。用 use namespace 開啟了命名空間以後,它會在所開啟的程式碼區塊之中保持為開放。您無法明確地關閉命名空間。
但是,具有多個開啟的命名空間,會增加名稱衝突的可能性。若您不想開啟命名空間,可以使用命名空間和名稱修飾語標點符號來限定方法或屬性名稱,以避免使用 use namespace 指令。例如,下列程式碼將示範如何用 example1 命名空間來限定名稱 myFunction():
example1::myFunction();
使用命名空間
您可以在屬於 ActionScript 3.0 一部分的 flash.utils.Proxy 類別中,找到用來防止名稱衝突之命名空間的實際範例。Proxy 類別是用來取代 ActionScript 2.0 中的 Object.__resolve 屬性,可以讓您在錯誤發生之前,先攔截未定義屬性或方法的參考。為了避免名稱衝突,Proxy 類別的所有方法都位於 flash_proxy 命名空間。
若要更瞭解如何使用 flash_proxy 命名空間,您必須先瞭解如何使用 Proxy 類別。Proxy 類別的功能只適用於繼承自它的類別。也就是說,若要針對物件使用 Proxy 類別的方法,該物件的類別定義必須擴充 Proxy 類別。例如,若要攔截對未定義方法的嘗試呼叫,您必須先擴充 Proxy 類別,然後覆寫 Proxy 類別的 callProperty() 方法。
您可能還記得,實作命名空間通常要經過定義、套用,然後參考命名空間的三步驟程序,但是由於您從未明確地呼叫任何 Proxy 類別方法,所以只會定義和套用 flash_proxy 命名空間,而不會有參考的程序。ActionScript 3.0 會定義 flash_proxy 命名空間,並將在 Proxy 類別中套用這個命名空間。您的程式碼只需要將 flash_proxy 命名空間套用至擴充 Proxy 類別的類別即可。
flash_proxy 命名空間在 flash.utils 套件中定義的方式與下列程序類似:
package flash.utils
{
public namespace flash_proxy;
}
命名空間套用至 Proxy 類別的方法,如下列 Proxy 類別中的摘錄所示:
public class Proxy
{
flash_proxy function callProperty(name:*, ... rest):*
flash_proxy function deleteProperty(name:*):Boolean
...
}
如下列程式碼所示,您必須先匯入 Proxy 類別和 flash_proxy 命名空間。然後,您必須宣告類別以擴充 Proxy 類別 (若要在嚴謹模式中編譯,也必須加入 dynamic 特質)。當您覆寫 callProperty() 方法時,必須使用 flash_proxy 命名空間。
package
{
import flash.utils.Proxy;
import flash.utils.flash_proxy;
dynamic class MyProxy extends Proxy
{
flash_proxy override function callProperty(name:*, ...rest):*
{
trace("method call intercepted: " + name);
}
}
}
若建立 MyProxy 類別的實體,並呼叫未定義的方法 (如下列範例中呼叫的 testing() 方法),則您的 Proxy 物件會攔截方法呼叫,並執行已遭覆寫之 callProperty() 方法中的陳述式 (在此範例中,則是簡單的 trace() 陳述式)。
var mySample:MyProxy = new MyProxy();
mySample.testing(); // method call intercepted: testing
在 flash_proxy 命名空間中擁有 Proxy 類別的方法,將有兩大優點:首先,獨立的命名空間可讓擴充 Proxy 類別的任何類別之公用介面減少堆積現象 (Proxy 類別中可供您覆寫的方法大約有十來個,這些全都不是設計用來直接進行呼叫的。因此,將它們全都放在公用命名空間中可能會造成混淆)。第二,如果 Proxy 子類別中所包含的實體方法名稱與任何 Proxy 類別方法完全相符,使用 flash_proxy 命名空間可以避免名稱衝突。例如,您可能要將自己的方法其中一個命名為 callProperty()。下列為可接受的程式碼,因為您的 callProperty() 方法版本是位於不同的命名空間中:
dynamic class MyProxy extends Proxy
{
public function callProperty() {}
flash_proxy override function callProperty(name:*, ...rest):*
{
trace("method call intercepted: " + name);
}
}
若您想要提供的存取權限無法以這四個存取控制指定字 (public、private、internal 和 protected) 取得時,命名空間可能也有幫助。舉例來說,您可能有一些公用程式方法,分散在幾個不同的套件中。您想要讓所有套件都能使用這些方法,但又不想讓方法變成公用方法。若要達到這項目的,您可以建立命名空間,然後用它做為您自己的特殊存取控制指定字。
下列範例會利用使用者定義的命名空間,將位於不同套件中的兩個函數組合在一起。藉由將這兩個函數組合在相同的命名空間中,您可以讓類別或套件透過單一 use namespace 陳述式同時看見兩個函數。
本範例會使用四個檔案來示範此技巧。所有檔案都必須位於您的類別路徑中。第一個檔案是 myInternal.as,用來定義 myInternal 命名空間。因為檔案是在名為 example 的套件中,所以您必須將檔案放入名為 example 的檔案夾中。該命名空間會標示為 public,以便能匯入其它套件中。
// myInternal.as in folder example
package example
{
public namespace myInternal = "http://www.adobe.com/2006/actionscript/examples";
}
第二個和第三個檔案分別是 Utility.as 和 Helper.as,定義包含必須供其它套件使用之方法的類別。Utility 類別是在 example.alpha 套件中,也就是說,檔案應該是在 example 檔案夾下名為 alpha 的子檔案夾中。Helper 類別是在 example.beta 套件中,也就是說,檔案應該是在 example 檔案夾下名為 beta 的子檔案夾之中。example.alpha 和 example.beta 這兩個套件都必須先匯入命名空間,才能加以使用。
// Utility.as in the example/alpha folder
package example.alpha
{
import example.myInternal;
public class Utility
{
private static var _taskCounter:int = 0;
public static function someTask()
{
_taskCounter++;
}
myInternal static function get taskCounter():int
{
return _taskCounter;
}
}
}
// Helper.as in the example/beta folder
package example.beta
{
import example.myInternal;
public class Helper
{
private static var _timeStamp:Date;
public static function someTask()
{
_timeStamp = new Date();
}
myInternal static function get lastCalled():Date
{
return _timeStamp;
}
}
}
第四個檔案 NamespaceUseCase.as 是主要的應用程式類別,必須是 example 檔案夾的同級節點。在 Flash Professional 中,這個類別是用來當做 FLA 的文件類別使用。NamespaceUseCase 類別也會匯入 myInternal 命名空間,並用它來呼叫兩個位於其它套件中的靜態方法。本範例之所以使用靜態方法,只是為了簡化程式碼而已。靜態和實體方法都可以放在 myInternal 命名空間中。
// NamespaceUseCase.as
package
{
import flash.display.MovieClip;
import example.myInternal; // import namespace
import example.alpha.Utility;// import Utility class
import example.beta.Helper;// import Helper class
public class NamespaceUseCase extends MovieClip
{
public function NamespaceUseCase()
{
use namespace myInternal;
Utility.someTask();
Utility.someTask();
trace(Utility.taskCounter); // 2
Helper.someTask();
trace(Helper.lastCalled); // [time someTask() was last called]
}
}
}