ActionScript3中的名字空间[namespace]
名字空间使你能够控制你创建的属性和行为的可见性。public, private, protected, and internal 这些访问控制符都是内置的名字空间.如何这些预定义的访问控制符不能满足你的需求,你就可以定义自己的名字空间.
如果你熟悉XML的名字空间的话,那么这里的一些讨论对你来说都不会陌生,当然,语法和细节方面,ActionScript实现起来和XML还是有些细微的差别 的. 如果你之前从未接触过名字空间, 概念本身是很简单的,但是你仍然需要仔细学习下使用细节.
首先要知道,一个属性或者行为的名字总是包括两部分:标识符和名字空间. 标识符就是平常认为的名字. 比如,在下面的类定义中标识符就是 sampleGreeting 和 sampleFunction():
class SampleCode
{
var sampleGreeting:String;
function sampleFunction () {
trace(sampleGreeting + " from sampleFunction()");
}
}
只要没有为定义加上名字空间属性, 它们的名字都默认属于 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的名字空间, 你可以使用example1作为属性把名为myFunction() 的函数添加进来, 如下所示:
namespace example1;
class someClass
{
example1 myFunction() {}
}
使用名字空间example1作为属性声明函数myFunction() 意思是该函数属于名字空间example1.
当应用名字空间的是有你应该记住下面几点:
- 对任何一个定义只能应用一个名字空间.
- 无法对多个定义同时应用一个名字空间. 换句话说,如果你想对10个不同的函数应用名字空间, 你只能对这10个函数一一添加你的名字空间属性.
- 如果你应用了名字空间,就不能再指定任何的访问控制符,因为名字空间和访问控制符是互斥的. 换句话说除了应用你的名字空间外,你不能声明一个函数或者属性为
public,private,protected,orinternal.
引用名字空间
当使用一个用访问控制名字空间,比如public, private, protected, and internal,声明的方法时,没有必要明确引用这个名字空间. 这是因为对这些特殊名字空间的访问由上下文来控制. 比如,放入到private名字空间中的定义自动对同一个类中的代码可用. 但是对你自定义的名字空间,这种上下文制约并不存在. 为了使用已经放入到自定义名字空间中的方法或者属性,你必须引用这个名字空间.
你可以直接使用use namespace引用名字空间,或者你可以使用名字空间加名字限定词(::)限定名字. 通过直接使用use namespace引用名字空间将 “打开” 该名字空间,然后它就可以应用到所有还未被限定的标识符. 比如,如果你已经定义了名字空间example1 ,你可以通过使用use namespace example1访问该名字空间中的名字:
use namespace example1; myFunction();
同一时间你可以打开多个名字空间. 一旦使用use namespace打开了名字空间, 它将在被打开的代码块范围内始终保持打开状态. 没有办法显式的关闭一个名字空间.
但是多个打卡的名字空间会增加名字冲突的可能. 如果你不希望打开一个名字空间,你可以使用名字空间加::限定方法或者属性名字的办法替代直接使用 use namespace . 比如,下面的代码展示了如何使用名字空间example1限定函数myFunction():
example1::myFunction();
使用名字空间
Flash Player API中的Flash.utils.Proxy类就是使用名字空间避免名字冲突的例子.Proxy类, 是ActionScript 2.0中的Object.__resolve属性的替代者, 允许在发生错误前中断对未定义属性或方法的引用. Proxy类的所有方法位于 flash_proxy名字空间中来避免命名冲突.
为了更好的理解flash_proxy名字空间是如何作用的, 首先要知道如何使用Proxy类. 只有继承自Proxy的类才能使用Proxy类的功能.换句话说, 如果想在一个对象上使用Proxy类的方法,那么这个对象的定义必须扩展Proxy类. 比如,如果想中断调用未定义方法的尝试, 你需要扩展Proxy类并且重写Proxy类的callProperty() 方法.
回想一下实现名字空间的3个步骤:定义、应用和引用名字空间. 但是,名字空间flash_proxy只是被定义、应用,却从未被引用过,这是因为我们从来没有明确的调用过Proxy类的任何方法. Flash Player API 在Proxy类的内部定义和应用名字空间 flash_proxy. 你的代码只需要对扩展自Proxy的类应用名字空间flash_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中有大约一打的方法可以被重写,这些方法不是设计成能直接调用的. 把它们都放在公共名字空间中会产生混淆.) 其次,如果你的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);
}
}
当使用4种访问控制符(public, private, internal, and protected)不能满足需求时,使用名字空间会很有用. 比如, 你可能拥有一些很有用的方法,但是这些方法被放在了不同的包中. 你想让这些方法对所有的包可用,但是又不想让这些方法成为public的. 在这种情况下,你可以创建一些名字空间,然后用它作为你自己的防伪控制符来达到访问控制的目标.
下面的例子使用用户定义的名字空间把两个位于不同包中的函数组合在一些. 通过把它们组合在相同的名字空间中,就可以使用use namespace语句使它们对类或者包可见.
这个例子使用4个文件演示这种技术. 所有的文件都必须位于类路径中. 第一个文件, myInternal.as, 用来定义名字空间myInternal. 因为这个文件位于名为example的包内,所以你必须把这个文件放到名为example的文件夹内. 这个名字空间被标记为public,因此,它可以被导入到任何包内.
// myInternal.as in folder example
package example
{
public namespace myInternal = "http://www.adobe.com/2006/actionscript/examples";
}
第2个和第3个文件, Utility.as and Helper.as, 定义了类,类中包含应该被其他包可用的方法.Utility类在example.alpha包中,也就是说,这个文件应该放在example文件夹内的alpha子文件夹内. Helper类位于example.beta包中,这个文件应该放在example文件夹内的beta子文件夹内.所有这些包, 包括example.alphaexample.alpha 和 example.beta, 都必须首先import名字空间后再使用它.
// 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;
}
}
}
第4个文件, NamespaceUseCase.as, 是主应用类, 应该放到example文件夹内.在Adobe Flash CS3 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]
}
}
}

Leave a Reply