TypeScript细碎知识点:命名空间(namespace)
一、前言
在js早起未出现“模块”这一概念以前,为了确保我们创建的变量不会泄露至全局变量中,我们以前曾采用过这种代码组织形式:
(function(someObj){ someObj.age = 18; })(someObj || someObj = {});
后来,js慢慢引入了文件模块的概念,通过export或者module.exports 暴露的方式,也可以解决了这个问题。
文件模块可以很好的解决不同文件间的重名问题,那么同一个文件中有时候也是避免不了重名问题,那又该如何解决呢?
我们知道“在TypeScript1.5版本之前有两种模块:内部模块和外部模块,而在1.5以后内部模块更名为命名空间,外部模块则更名为模块”。而这个命名空间就可以很好的解决同一个文件中的重名问题,或者干脆说命名空间就是可以解决重名问题不管是同一文件还是不同的文件,只要不是同一个命名空间即可。
下面我们就来看下命名空间又是个啥?
前面已经提到TypeScript中的命名空间是由原来的内部模块更名而来,它的最明确的目的就是为了解决重名问题。了解了命名空间的作用,下面我们再来看下,命名空间是如何使用,它都有哪些特点:
⏰ 1. 命名空间是通过关键字 **“namespace”** + “名称” 来定义的
//这就在文件模块里面定义了一个命名空间,空间名"MySpace" namespace MySpace { }
⏰ 2. 同一个文件中可以定义多个相同或不同的命名空间
//1.命名空间一 namespace MySpaceOne { export const a = 123; } //2.命名空间二 namespace MySpaceTwo { export const a = 123; } //3.命名空间三 namespace MYSpaceThree { export const a = 123; }
⏰ 3. 同一个命名空间也可以分割在不同的文件中定义,只要名称相同都认为它们是同一个命名空间
-
定义不同名变量时
//a.ts namespace mySpace { export const a = 123; }
//b.ts namespace mySpace { export const b = 123; }
-
定义相同名变量时
//a.ts namespace mySpace { export const a = 123; //🙅错误: 无法重新声明块范围变量“a”。因为b.ts(3, 18): 此处也声明了 "a"。 }
//b.ts namespace mySpace { export const a = 123; //🙅错误: 无法重新声明块范围变量“a”。因为 a.ts(3, 18): 此处也声明了 "a"。 }
⏰ 4. 如果想在一个文件中引用其它文件中的命名空间时,需要通过三斜杠加reference标签的形式引入
-
在a.ts 中声明一个类型
//a.ts namespace mySpace { export interface myInterface { name: string, age: number } }
-
在b.ts 中创建这个类型的对象
//b.ts ///<reference path = "a.ts"/> const myInterface: mySpace.myInterface = { name:'namespace', age:18 }
-
效果
⏰ 5. 如果想在命名空间以外使用命名空间中的内容(变量、函数、类、接口等)时,被使用的内容必须要借助export导出,否则无法使用,也就是说只有通过export导出的内容才可以在命名空间外访问
-
命名空间里定义变量,函数, 类, 接口
namespace mySpace { //1.命名空间里定义了类型 export interface myInterface { name: string, age: number } //2.命名空间里定义了变量 export const a = 123; //3.命名空间里定义了类 export class myClass { name:string; age:number } //4.命名空间里定义了函数 export function myFunction(){ } }
-
注意
⏰ 6. 访问同一个命名空间中的变量、函数、类名等内容时可以直接访问
-
在 a.ts 文件里面 定义
//a.ts namespace mySpace { //1.命名空间里定义了类型 export interface myInterface { name: string, age: number } //2.命名空间里定义了变量 export const a = 123; //3.命名空间里定义了类 export class myClass { name: string; age: number constructor(name: string, age: number) { this.name = name; this.age = age } } //4.命名空间里定义了函数 export function myFunction() { console.log('我是命名空间里定义的函数') } }
-
在 b.ts 文件里面 调用
//b.ts /**虽然在不同的文件里面,但是是在同一命名空间下,所以可以直接使用,无需引用**/ namespace mySpace { //1.接口 const myInterfaces: myInterface = { name : 'nameSpace', age : 18 } //2.变量 console.log(a) //3.类 const myClasses = new myClass('123',10); //4.函数 myFunction(); }
⏰ 7. 如果访问不同命名空间中的变量、函数、类名等内容时,除了这些内容要通过export导出外,还需通过 命名空间.xxx 的形式进行访问
-
a.ts
//a.ts //【定义】 namespace mySpace { //1.命名空间里定义了类型 export interface myInterface { name: string, age: number } //2.命名空间里定义了变量 export const a = 123; //3.命名空间里定义了类 export class myClass { name: string; age: number constructor(name: string, age: number) { this.name = name; this.age = age } } //4.命名空间里定义了函数 export function myFunction() { console.log('我是命名空间里定义的函数') } } //【使用】 //1.接口 const myInterfaces: mySpace.myInterface = { name: 'nameSpace', age: 18 } //2.变量 console.log(mySpace.a) //3.类 const myClasses = new mySpace.myClass('123', 10); //4.函数 mySpace.myFunction();
-
注意
⏰ 8. 命名空间可以层层嵌套,就是说一个命名空间中可以包含其他命名空间,访问时同样也是用命名空间.命名空间.xxx 的形式进行访问,内层的命名空间也需要被export。
-
a.ts
//a.ts //【定义】 namespace mySpaceOuter { export namespace mySpaceInner { //1.命名空间里定义了类型 export interface myInterface { name: string, age: number } //2.命名空间里定义了变量 export const a = 123; //3.命名空间里定义了类 export class myClass { name: string; age: number constructor(name: string, age: number) { this.name = name; this.age = age } } //4.命名空间里定义了函数 export function myFunction() { console.log('我是命名空间里定义的函数') } } } //【使用】 //1.接口 const myInterfaces: mySpaceOuter.mySpaceInner.myInterface = { name: 'nameSpace', age: 18 } //2.变量 console.log(mySpaceOuter.mySpaceInner.a) //3.类 const myClasses = new mySpaceOuter.mySpaceInner.myClass('123', 10); //4.函数 mySpaceOuter.mySpaceInner.myFunction();
-
注意
二、命名空间的声明
TypeScript 的命名空间只对外暴露需要在外部访问的对象,命名空间内的对象通过 export 关键字对外暴露,比如我们在一个名叫 utils.ts
的文件里声明一个命名空间:
// utils.ts namespace Utils { export interface IPerson { name: string; age: number; } }
三、命名空间的使用
通过 namespace 关键字声明命名空间,在命名空间外部需要通过完全限定名访问这些对象,通常情况下,声明的命名空间代码和调用的代码不在同一个文件里,因此在其他文件中使用,注意引入的路径要写正确,此处我们在同级目录中任意一个 ts 文件中使用我们刚定义的命名空间:
/// <reference path="utils.ts" /> const me: Utils.IPerson = { name: 'funlee', age: 18 } console.log(me); // {name: 'funlee', age: 18}
如上述代码所示,通过 reference 注释引用命名空间,即可通过完全限定名进行访问,我们也可以通过 import
导入模块的形式,引入命名空间:
import './utils' const me: Utils.IPerson = { name: 'funlee', age: 18 } console.log(me); // {name: 'funlee', age: 18}
四、多文件的命名空间
就像普通的 JS 模块文件可以相互引用一样,包含 namespace 的命名空间文件也可以相互引入,还可以组合成一个更大的命名空间,下面是一个简单的示例,所有文件都在同一目录下,你也可参考官方示例:
-
utils.ts
namespace Utils { export interface IAnimal { name: string; say(): void; } }
-
animal.ts
/// <reference path="utils.ts" /> export namespace Animal { export class Dog implements Utils.IAnimal{ name: string; constructor(theName: string) { this.name = theName; } say() { console.log(`${this.name}: 汪汪汪`) } } }
- index.ts
import {Animal} from './animal'; const he = new Animal.Dog('Jack'); he.say(); // Jack: 汪汪汪