Candy Cup Analogy
版本1:每个糖果都有一个杯子
假设您编写了如下代码:
Mod1.ts个个
export namespace A {
export class Twix { ... }
}
Mod2.ts
export namespace A {
export class PeanutButterCup { ... }
}
Mod3.ts
export namespace A {
export class KitKat { ... }
}
You've created this setup:
每个模块(一张纸)都有its own cup个名为A
的模块.这是没有用的——你实际上并不是在这里吃糖果,你只是在你和糖果之间增加了一个额外的步骤(从杯子中取出).
版本2:全局的一个杯子
如果不使用模块,可能会编写这样的代码(注意缺少export
个声明):
global1.ts个
namespace A {
export class Twix { ... }
}
global2.ts个
namespace A {
export class PeanutButterCup { ... }
}
global3.ts个
namespace A {
export class KitKat { ... }
}
This代码在全局作用域中创建合并的命名空间A
:
这种设置很有用,但不适用于模块(因为模块不会污染全局范围).
版本3:无杯化
回到最初的例子,杯子A
、A
和A
对您没有任何帮助.相反,您可以将代码编写为:
Mod1.ts个个
export class Twix { ... }
Mod2.ts
export class PeanutButterCup { ... }
Mod3.ts
export class KitKat { ... }
要创建如下所示的图片:
好多了!
现在,如果您仍在思考您到底有多希望在模块中使用名称空间,请继续阅读...
These Aren't the Concepts You're Looking For
我们首先需要回到名称空间存在的原因,并判断这些原因对于外部模块是否有意义.
Organization:名称空间便于将逻辑相关的对象和类型组合在一起.例如,在C#中,您将在System.Collections
中找到所有集合类型.通过将类型组织到分层名称空间中,我们为这些类型的用户提供了良好的"发现"体验.
Name Conflicts:名称空间对于避免命名冲突很重要.例如,您可能有My.Application.Customer.AddForm
和My.Application.Order.AddForm
——这两种类型具有相同的名称,但名称空间不同.在一种语言中,所有标识符都存在于同一根作用域中,所有程序集都加载所有类型,因此将所有内容都放在名称空间中是至关重要的.
这些原因在外部模块中有意义吗?
Organization:外部模块必须已经存在于文件系统中.我们必须通过路径和文件名来解析它们,所以我们可以使用一个逻辑组织方案.我们可以有一个包含list
模块的/collections/generic/
文件夹.
Name Conflicts:这完全不适用于外部模块.Within一个模块,没有合理的理由让两个对象具有相同的名称.从消费端来看,任何给定模块的用户都可以 Select 他们将用来引用该模块的名称,因此不可能出现意外的命名冲突.
即使您不认为模块的工作方式充分解决了这些原因,但try 在外部模块中使用名称空间的"解决方案"甚至都不起作用.
盒子里的盒子里的盒子
一个故事:
你的朋友鲍勃给你打了电话."我家里有一个很棒的新组织方案,"他说,"快来看看!"奈特,我们go 看看鲍勃想出了什么.
你从厨房开始,打开储藏室.有60个不同的盒子,每个盒子都贴着"食品储藏室"的标签.你随意挑选一个盒子,然后打开它.里面有一个单独的盒子,上面贴着"谷物"的标签.你打开"谷物"盒子,发现一个标有"意大利面"的盒子.你打开"意大利面"的盒子,发现只有一个标有"Penne"的盒子.你打开这个盒子,不出所料,发现了一袋便士意大利面.
有点困惑,你拿起一个相邻的盒子,上面也写着"餐具室".里面是一个单独的盒子,再次贴上"谷物"的标签.你打开"谷物"盒子,再次找到一个标有"意大利面"的盒子.你打开"意大利面"盒子,找到一个盒子,这个盒子的标签是"里加通心粉".你打开这个盒子,发现...一袋里加通心粉.
"太棒了!"鲍勃说."一切都在一个命名空间中!".
"但是鲍勃."你回答."你的组织方案是没用的.你必须打开一堆盒子才能拿到任何东西,而且实际上找什么都不比你把所有东西都放在one个盒子里而不是three个盒子里更方便.事实上,既然你的食品储藏室已经逐个货架分类了,你根本不需要这些盒子.为什么不把意大利面放在架子上,需要的时候再捡起来呢?"
"你不明白--我需要确保没有人把不属于‘食品储藏室’命名空间的东西放进‘食品储藏室’.而且我已经安全地把我所有的意大利面都组织到了Pantry.Grains.Pasta
命名空间中,这样我就可以很容易地找到它."
鲍勃是一个非常困惑的人.
模块是它们自己的盒子
你可能在现实生活中也发生过类似的事情:你在亚马逊上订购了几样东西,每件商品都会出现在自己的盒子里,里面有一个更小的盒子,你的商品包装在自己的包装里.即使内部箱子相似,装运的Cargo 也没有有效的"组合".
按照盒子的比喻,关键的观察是external modules are their own box.它可能是一个非常复杂的项目,有很多功能,但是任何给定的外部模块都是它自己的盒子.
Guidance for External Modules
既然我们已经知道我们不需要使用"名称空间",那么我们应该如何组织我们的模块呢?下面是一些指导原则和例子.
出口尽可能接近顶级水平
- 如果只导出单个类或函数,请使用
export default
:
MyClass.ts
export default class SomeType {
constructor() { ... }
}
MyFunc.ts
function getThing() { return 'thing'; }
export default getThing;
Consumption
import t from './MyClass';
import f from './MyFunc';
var x = new t();
console.log(f());
这对消费者来说是最佳 Select .他们可以随心所欲地命名你的类型(本例中为t
),并且不必做任何无关的点操作来找到你的对象.
MyThings.ts
export class SomeType { ... }
export function someFunc() { ... }
Consumption
import * as m from './MyThings';
var x = new m.SomeType();
var y = m.someFunc();
- 如果你要导出大量的东西,只有这样你才应该使用
module
/namespace
关键字:
MyLargeModule.ts
export namespace Animals {
export class Dog { ... }
export class Cat { ... }
}
export namespace Plants {
export class Tree { ... }
}
Consumption
import { Animals, Plants} from './MyLargeModule';
var x = new Animals.Dog();
Red Flags
以下所有内容都是模块 struct 的危险信号.如果其中任何一项适用于您的文件,请仔细判断您没有try 为外部模块设置命名空间:
- 唯一顶级声明为
export module Foo { ... }
的文件(删除Foo
并将所有内容"上移"一级)
- 包含单个
export class
或export function
而不是export default
的文件
- 多个文件在顶层有相同的
export module Foo {
个(不要认为这些文件会合并成一个Foo
!)