随着软件中第三方库的使用越来越多,我们经常会遇到全局命名空间被污染的问题,导致全局命名空间中组件之间的名称冲突。因此,我们需要使用命名空间来组织代码块,以便唯一标识变量、对象和类。

在本文中,我们将讨论命名空间、何时需要它们以及如何使用它们来增强 TypeScript 代码的组织。

什么是命名空间?

命名空间是组织代码的范例,以便变量、函数、接口或类在局部范围内组合在一起,以避免全局范围内组件之间的命名冲突。这是减少全局范围污染的最常见策略之一。

虽然模块也用于代码组织,但命名空间很容易用于简单的实现。模块提供了一些额外的好处,例如强大的代码隔离、对打包的强大支持、组件的重新导。

为什么我们需要命名空间?

命名空间具有以下优点:

  • 代码可重用性——不能低估命名空间对代码可重用性的重要性
  • 膨胀的全局作用域——命名空间减少了全局作用域中的代码量,使其不那么臃肿
  • 第三方库——随着依赖第三方库的网站数量不断增加,使用命名空间保护您的代码以防止您的代码和第三方库之间的同名冲突非常重要
  • 分布式开发——随着分布式开发的流行,污染几乎是不可避免的,因为开发人员可以更容易地使用公共变量或类名。这会导致名称冲突和全局范围的污染

使用命名空间的设计注意事项

隐式依赖顺序

在使用某些外部库时使用命名空间将需要在您的代码和这些库之间隐式实现依赖关系。这导致您自己管理依赖项以便正确加载它们的压力,因为依赖项可能容易出错。如果您发现自己处于这种情况,使用模块可以减轻您的压力。

Node.js 应用程序

对于Node.js 应用程序,建议使用模块而不是命名空间,因为模块是 Node 中封装和代码组织的标准。

非JavaScript内容导入

在处理非 JavaScript 内容时,推荐使用模块而不是命名空间,因为一些模块加载器(例如 SystemJS 和 AMD)允许导入非 JavaScript 内容。

遗留代码

当使用不再设计但不断修补的代码库时,建议使用命名空间而不是模块。

此外,在移植旧的 JavaScript 代码时,命名空间会派上用场。

探索 TypeScript 中的命名空间

现在我们对 TypeScript 命名空间是什么以及我们为什么需要它们有了共同的理解,我们可以更深入地了解如何使用它们。

鉴于 TypeScript 是 JavaScript 的超集,它的命名空间概念源自 JavaScript。

默认情况下,JavaScript 没有命名空间,因为我们必须使用 IIFE(立即调用函数表达式)来实现命名空间:

var Vehicle;
(function (Vehicle) {
    let name = "car";
})(Vehicle || (Vehicle = {}));

这是用于定义命名空间的大量代码。同时,TypeScript 的处理方式有所不同。

单文件命名空间

在 TypeScript 中,命名空间是使用namespace关键字后跟选择的名称来定义的。

单个 TypeScript 文件可以根据需要拥有多个命名空间:

namespace Vehicle {}
namespace Animal {}

正如我们所看到的,与我们使用IIFE的命名空间的JavaScript实现相比,TypeScript 命名空间是一块语法糖。

函数、变量和类可以在命名空间内定义如下:

namespace Vehicle {
  const name = "Toyota"
  function getName () {
      return `${name}`
  }
}
namespace Animal {
  const name = "Panda"
  function getName () {
      return `${name}`
  }
}

上面的代码允许我们使用相同的变量和函数名而不会发生冲突。

访问命名空间外的函数、变量、对象和类

为了访问其命名空间之外的函数或类,export必须在函数或类名之前添加关键字,如下所示:

namespace Vehicle {
  const name = "Toyota"
  export function getName () {
      return `${name}`
  }
}

请注意,我们必须省略export变量的关键字,因为它不应在名称空间之外访问。

现在,我们可以getName按如下方式访问该函数:

Vehicle.getName() //Toyota

使用嵌套命名空间组织代码

TypeScript 允许我们使用嵌套命名空间来组织我们的代码。

我们可以创建嵌套的命名空间,如下所示:

namespace TransportMeans {
  export namespace Vehicle {
    const name = "Toyota"
    export function getName () {
        return `${name}`
    }
  }
}

注意命名空间export前的关键字Vehicle。这允许在命名空间之外访问TransportMeans命名空间。

我们还可以执行命名空间的深度嵌套。

我们的嵌套命名空间可以按如下方式访问:

TransporMeans.Vehicle.getName() // Toyota

命名空间别名

对于深度嵌套的命名空间,命名空间别名可以派上用场以保持整洁。

命名空间别名是使用 import 关键字定义的,如下所示:

导入汽车名称=运输方式。车辆;车名。获取名称(); //丰田

import carName= TransporMeans.Vehicle;
carName.getName(); //Toyota

多文件命名空间

命名空间可以在多个 TypeScript 文件之间共享。这是通过reference标签实现的。

考虑到以下几点:

//constant.ts
export const name = "Toyota"


//vehicle.ts
<reference path = "constant.ts" />
export namespace Vehicle {
  export function getName () {
      return `${name}`
  }
}

在这里,我们必须引用constant.ts文件才能访问name

//index.ts
<reference path = "constant.ts" />
<reference path = "vehicle.ts" />
Vehicle.getName() // Toyota

请注意我们如何使用最高级别的命名空间开始我们的引用。这就是如何处理多文件接口中的引用。TypeScript 将在编译文件时使用此顺序。

我们可以使用以下命令指示编译器将我们的多文件 TypeScript 代码编译成单个 JavaScript 文件:

 tsc --outFile index.js index.ts

使用此命令,TypeScript 编译器将生成一个文件名为index.js

结论

为了构建可扩展和可重用的 TypeScript 应用程序,TypeScript 命名空间非常方便,因为它们改进了我们应用程序的组织和结构。