通过命名空间可以控制所创建的属性和方法的可见性。请将 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 可确保命名空间定义的唯一性。
可通过使用以下两种方法之一来声明命名空间定义,以创建命名空间:像定义 XML 命名空间那样使用显式 URI 定义命名空间;省略 URI。下面的示例说明如何使用 URI 来定义命名空间:
namespace flash_proxy = "http://www.adobe.com/flash/proxy";
URI 用作该命名空间的唯一标识字符串。如果您省略 URI(如下面的示例所示),编译器将创建一个唯一的内部标识字符串来代替 URI。您对于这个内部标识字符串不具有访问权限。
namespace flash_proxy;
在定义了命名空间(具有 URI 或没有 URI)后,就不能在同一个作用域内重新定义该命名空间。如果尝试定义的命名空间以前在同一个作用域内定义过,则将生成编译器错误。
如果在某个包或类中定义了一个命名空间,则该命名空间可能对于此包或类外部的代码不可见,除非使用了相应的访问控制说明符。例如,下面的代码显示了在 flash.utils 包中定义的 flash_proxy 命名空间。下面的示例中没有使用访问控制说明符,这意味着 flash_proxy 命名空间仅对于 flash.utils 包内部的代码可见,而对于该包外部的任何代码都不可见:
package flash.utils
{
namespace flash_proxy;
}
下面的代码使用 public 属性使 flash_proxy 命名空间对该包外部的代码可见:
package flash.utils
{
public namespace flash_proxy;
}
应用命名空间
应用命名空间意味着在命名空间中放置定义。可以放在命名空间中的定义包括函数、变量和常量(不能将类放在自定义命名空间中)。
例如,请考虑一个使用 public 访问控制命名空间声明的函数。在函数的定义中使用 public 属性会将该函数放在 public 命名空间中,从而使该函数对于所有的代码都可用。在定义命名空间之后,可以按照与使用 public 属性相同的方式来使用所定义的命名空间,该定义对于可以引用您的自定义命名空间的代码可用。例如,如果您定义一个名为 example1 的命名空间,则可以添加一个名为 myFunction() 的方法并将 example1 用作属性,如下面的示例所示:
namespace example1;
class someClass
{
example1 myFunction() {}
}
如果在声明 myFunction() 方法时将 example1 命名空间用作属性,则意味着该方法属于 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
将 Proxy 类的方法放在 flash_proxy 命名空间内部有两个好处。第一个好处是,在扩展 Proxy 类的任何类的公共接口中,拥有单独的命名空间可提高代码的可读性。(在 Proxy 类中大约有 12 个可以覆盖的方法,所有这些方法都不能直接调用。将所有这些方法都放在公共命名空间中可能会引起混淆。)第二个好处是,当 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]
}
}
}