1.前言
在TypeScript官方文档中是这样描述命名空间的:
请务必注意一点,TypeScript 1.5里术语名已经发生了变化。 “内部模块”现在称做“命名空间”。 “外部模块”现在则简称为“模块”,这是为了与 ECMAScript 2015里的术语保持一致,(也就是说 module X { 相当于现在推荐的写法 namespace X {)。
这段话简单看就是,现在“内部模块”被叫命名空间,使用的关键词为namespace;“外部模块”现在被叫做模块,使用关键词module
2.命名空间的使用
命名空间的使用方式非常简单:
namespace XXX {}
在官方文档中namespace的使用例子如下
//创建一个命名空间Validation
namespace Validation {
//将命名空间中的接口暴露出去
export interface StringValidator {
isAcceptable(s: string): boolean;
}
const lettersRegexp = /^[A-Za-z]+$/;
const numberRegexp = /^[0-9]+$/;
//将LettersOnllyValidator类暴露出去
export class LettersOnlyValidator implements StringValidator {
isAcceptable(s: string) {
return lettersRegexp.test(s);
}
}
//将ZipCodeValidator类暴露出去
export class ZipCodeValidator implements StringValidator {
isAcceptable(s: string) {
return s.length === 5 && numberRegexp.test(s);
}
}
}
let strings = ["Hello", "98052", "101"];
let validators: { [s: string]: Validation.StringValidator; } = {};
validators["ZIP code"] = new Validation.ZipCodeValidator();
validators["Letters only"] = new Validation.LettersOnlyValidator();
for (let s of strings) {
for (let name in validators) {
console.log(`"${ s }" - ${ validators[name].isAcceptable(s) ? "matches" : "does not match" } ${ name }`);
}
}
- 该例子创建了一个名为
Validation的命名空间,在这个命名空间中有一个StringValidator接口,并有两个类分别是LettersOnlyValidator类和ZipCodeValidator类,这两个类分别用来判断是否为英文字母和是否为0-9的数字。 - 当我们想让这些接口和类在命名空间之外也是可访问的,所以需要使用
export。 相反的,变量lettersRegexp和numberRegexp是实现的细节,不需要导出,因此它们在命名空间外是不能访问的。 在文件末尾的测试代码里,由于是在命名空间之外访问,因此需要限定类型的名称,比如Validation.LettersOnlyValidator。
从上面的话中我们可以看出,在命名空间中,可以隔绝作用域,外部的作用域无法访问到Validation中的作用域,如果想让外面的作用域访问到Validation中的作用域,需要使用export将想要访问的接口或是类给暴露出去。
- 在命名空间中,有他自己的作用域,外部无法访问到,要想访问需使用
export将访问的部分暴露出去
3.每个命名空间中的作用域都是独立的
// namespace 定义一个名为 A 的单独的空间
namespace A {
interface Animal{
name:string;
eat():void;
}
// 外部需要调用的类 需要用 【export】 给 导出 去
export class Dog implements Animal {
name: string;
constructor(name:string) {
this.name = name;
}
eat() {
return this.name + '吃骨头1'
}
}
}
// namespace 定义一个名为 V 的单独的空间
namespace V {
interface Animal{
name:string;
eat():void;
}
// 外部需要调用的类 需要用 【export】 给 导出 去
export class Dog implements Animal {
name: string;
constructor(name:string) {
this.name = name;
}
eat() {
return this.name + '吃骨头2'
}
}
}
let dog1 = new A.Dog('小黑')
console.log(dog1.eat()) // 小黑吃骨头1
let dog2 = new V.Dog('小白')
console.log(dog2.eat()) // 小白吃骨头2
从上例子可以看出,每个命名空间都是相互独立的
4. 同名命名空间的合并
namespace Animals {
export class Cat { }
}
namespace Animals {
export interface Legged { numberOfLegs: number; }
export class Dog { }
}
以上代码等同于
namespace Animals {
export interface Legged { numberOfLegs: number; }
export class Cat { }
export class Dog { }
}
在相同的名字的命名空间中
- 其中模块导出的同名接口会合并为一个接口
- 未导出的成员,仅在未合并前的命名空间可见。从其他空间合并来的成员无法访问未导出的成员
- 对于里头值的合并,如果里头值的名字相同,那么后来的命名空间的值会优先级会更高
- 对于没有冲突的成员,会直接混入
5.总结
- 命名空间有自己的作用域,外部无法访问到。当命名空间使用
export暴露出去时,外部可以访问到暴露的部分。当使用export将命名空间整个暴露出去时,外部需要使用的类或接口也要使用export在命名空间中暴露出去
对于同名命名空间来说:
- 其中模块导出的同名接口会合并为一个接口
- 未导出的成员,仅在未合并前的命名空间可见。从其他空间合并来的成员无法访问未导出的成员
- 对于里头值的合并,如果里头值的名字相同,那么后来的命名空间的值会优先级会更高
- 对于没有冲突的成员,会直接混入