public、private、protected、internal都是命名空间,如果我们要做一个类库,可能会需要这样的访问权限:库内可访问,这时就需要使用namespace自定义访问修饰符来实现。
adobe的教程写的相当详细。关键是还有中文版。
当在ActionScript中对方法和属性进行声明时,它们可以使用public、private、protected和 internal等访问修饰符来进行声明。 这些修饰符在大多数情况下都能够很好地实现目标;但是,有时你也需要额外的粒度(additional granularity)。 ActionScript使用一个名称为命名空间(namespace)的概念来解决该问题。 在本文中,你将学到什么是命名空间以及如何在你的应用程序中定义一个可以使用的命名空间。 你还将学习应用和引用命名空间来控制对方法和属性的访问权限。
命名空间是一组唯一的名称。 你可以使用命名空间来控制标识符的可见性,例如属性和方法名称。 每当你创建一个包时,你将创建一个命名空间,然而你还可以明确地定义一个命名空间并且可以在不管代码的包情况下将它应用于该代码。 命名空间允许你摆脱由包强加的组织结构并且不受访问控制属性的限制。
命名空间包含了一个值,即统一资源标识符(Uniform Resource Identifier (URI)),有时也称它为命名空间名称(namespace name)。 URI可以让你确保你的命名空间定义是独一无二的。
通过使用下面两种方法中的一种来声明一个命名空间定义,你可以创建一个命名空间。 你可以使用一个显式URI或者你可以忽略URI来定义一个命名空间。 下面的范例显示如何使用URI定义一个命名空间:
namespace flash_proxy = “http://www.adobe.com/flash/proxy”;
URI可以用作命名空间的一个唯一标识字符串。 如果你忽略URI,如下面的范例所示,那么编译器会在URI的位置上创建一个唯一的内部标识字符串。 你对这个内部标识字符串是没有访问权限的。
namespace flash_proxy;
一旦你定义一个命名空间,不论是使用还是没有使用URI,该命名空间都不能在相同的作用域内重新定义。 尝试在相同的作用域内定义一个较早之前就已经定义的命名空间会产生一个编译错误。
如果某个命名空间是在包或者类中定义的,那么该命名空间在包或者类之外是不可见的,除非使用了合适的访问控制指示语(access control specifier)。 例如,下面的代码显示了在flash.utils包中定义的命名空间。 访问控制属性关键字的缺少表明命名空间只对在flash.utils包中的代码可见:
package flash.utils {
namespace flash_proxy;
}
下面的代码使用属性使得命名空间在包之外对代码可见:
package flash.utils{
public namespace flash_proxy;
}
应用命名空间意味着在命名空间中放置定义。 能够置于命名空间的定义包括函数、变量以及常量(你不能将某个类置于自定义命名空间中)。
例如,考虑一下使用访问控制命名空间声明的一个函数。 使用函数定义中的属性将该函数置于命名空间,这样就使得该函数对所有代码均可用。 一旦你定义了一个命名空间,你就能够像你使用属性那样使用它,并且该定义也会对那些能够引用你的自定义命名空间的代码可用。 例如,如果你定义一个命名空间example1
,那么你可以使用example1
将名称为myFunction()
的方法添加为一种属性,如下面的范例所示:
namespace example1;
class someClass {
example1 myFunction() {}
}
使用命名空间example1
将 myFunction()
方法作为一种属性进行声明意味着该方法属于example1
命名空间。
当应用命名空间时,请记住下面提示:
你可以给每一个声明应用唯一的命名空间。
不能够同时给多个定义应用同一个命名空间属性。 如果你希望将你的命名空间应用于十个不同的函数,那么你必须将你的命名空间作为属性分别地添加到十个函数定义中。
如果你应用一个命名空间,那么你也不能指定一个访问控制指示语,这是因为命名空间和访问控制指示语是互斥的。 换句话说,除了应用你的命名空间之外,你不能以
public
,private
,protected
或者internal
来声明一个函数或者属性。
当你使用由任何一个访问控制属性(public
, private
, protected
以及internal
)声明的方法或者属性时,你不必显式地引用一个命名空间,因为相应的语境(context)控制了对这些特殊的命名空间的访问权限。 例如,置于private
命名空间的定义对于处在相同类中的代码是自动可用的。 但是,对于你定义的命名空间,这种语境敏感性(context sensitivity)并不存在。 为了使用你已置于某个自定义命名空间中的方法或者属性,那么你必须引用该命名空间。
你可以使用 use
namespace
指令引用命名空间,或者你可以使用具有名称限定符(::
)标点(punctuator)的命名空间来限定该名称。 使用use
namespace
指令引用一个命名空间能够“打开”该命名空间,因此它能够应用于任何未限定的标识符。 例如,如果你已经定义example1
命名空间,那么你可以使用use
namespace
example1
来访问该命名空间中的名称:
use namespace example1;
myFunction();
你可以同时打开多个命名空间。 一旦你使用use
namespace
打开一个命名空间,它就会在打开它的代码块中保持打开状态。 你不能够显式地关闭一个命名空间。
但是,具有多个开放的命名空间会增加名称冲突的可能性。 如果你不喜欢打开一个命名空间,那么你可以通过限定方法或者属性名称来避免使用use
namespace
指令,而方法或者属性名称均带有命名空间和名称限定符标点(name qualifier punctuator)。 例如,下面的代码显示如何使用example1
命名空间限定名称myFunction()
:
example1::myFunction();
你可以找到一个真实的命名空间范例,它可以用于防止flash.utils.Proxy类中的名称冲突,而flash.utils.Proxy类是ActionScript 3的一个组成部分。 作为替代来源于ActionScript 2的Object.__resolve
属性的Proxy类,它允许你在错误发生前拦截对未定义属性或者方法的引用。 Proxy类的所有方法均驻留在flash_proxy
命名空间中以防止名称冲突。
为了更好地了解flash_proxy
命名空间的使用方法,首先应该了解Proxy类的使用方法。 Proxy类的功能只对从它继承而来的类可用。 换句话说,如果你想在某个对象上使用该Proxy 类的方法,那么该对象的类定义必须扩展该Proxy类。 例如,如果你想拦截调用未定义方法的尝试行为,那么你必须扩展Proxy类,然后覆盖Proxy 类的callProperty()
方法。
你可能记得,实现命名空间通常是一个包括定义、应用和引用命名空间的三步骤过程。 但是,由于你从未显式地调用任何Proxy类方法,因此flash_proxy
命名空间只进行了定义和应用,而从未进行引用。 ActionScript 3可以定义flash_proxy
命名空间并在Proxy类中应用它。 你的代码只需要将flash_proxy
命名空间应用于可以扩展Proxy 类的类即可。
你能够以与下面相似的方式在flash.utils包中定义flash_proxy
命名空间:
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类(如果你是在严格模式(strict mode)下进行编译,那么你还必须添加flash_proxy
属性。)。 当你覆盖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类中大约有十几个你可以覆盖的方法,所有这些方法都不是设计用来直接调用的。 将它们全部都置于公共命名空间会产生混淆。) 第二,flash_proxy
命名空间的使用能够避免名称冲突,以防你的Proxy 子类包含的实例方法具有与任何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包中,这意味着该文件应该位于一个名称为alpha的文件夹中,该文件夹是example文件夹的子文件夹。 Helper 类位于example.beta a包中,这意味着该文件应该位于一个名称为beta 的文件夹中,该文件夹是example文件夹的子文件夹。 这两个包,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的document类。 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]
}
}
}
命名空间允许使用基于非包(non-package)和非继承(non-inheritance)的方式来控制对对象的方法和属性的访问。 它们已经在较大型的ActionScript项目内部广泛使用,其中包括Adobe Flex等组件框架。 此外,命名空间还能够与方法和属性组合使用。 你可以通过阅读对象简介(Introduction to objects)、类简介(Introduction to Classes)以及利用ActionScript 3编写类(Writing classes in ActionScript 3 )(它们均将在10月31发行)等资料来了解关于它们的更多些信息。
本文内容是基于最早出版的学习ActionScript 3用户指南(Learning ActionScript 3 user guide)中的材料,该材料是由Adobe社区帮助和学习(Adobe Community Help and Learning)编写的。
发表评论:
◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。