TypeScript 命名空间那些事儿

184 阅读3分钟

哈喽,大家好,我是 SuperYing。今天我们来聊聊 TypeScript 命名空间那些事儿。

关于术语的一点说明:
TypeScript 1.5里术语名已经发生了变化。 “内部模块”现在称做“命名空间”。 “外部模块”现在则简称为“模块”,这是为了与 ECMAScript 2015 里的术语保持一致,(也就是说 module X { 相当于现在推荐的写法 namespace X {)。

什么是命名空间

相信开头术语说明部分已经描述的很清楚了,命名空间就是模块,更具体一点称作内部模块

命名空间使用

这里我们借用一下官方的案例: 在上一篇文章TypeScript 模块那些事儿 中我们了解了模块的用法,那我假设在一个文件模块中定义几个表单验证器,用来验证表单输入或外部数据:

interface StringValidator {
    isAcceptable(s: string): boolean;
}

let lettersRegexp = /^[A-Za-z]+$/;
let numberRegexp = /^[0-9]+$/;

class LettersOnlyValidator implements StringValidator {
    isAcceptable(s: string) {
        return lettersRegexp.test(s);
    }
}

class ZipCodeValidator implements StringValidator {
    isAcceptable(s: string) {
        return s.length === 5 && numberRegexp.test(s);
    }
}

按理来说将这样的验证器放在一个文件模块中没有任何问题,实际上也没有问题,哈哈哈....但是随着验证场景逐渐增多,验证器的数量也会越来越多,这时为了避免命名冲突,也能更好的组织代码,就可以将相同类型的验证器包括在同一个命名空间内。

namespace Validation {
    export interface StringValidator {
        isAcceptable(s: string): boolean;
    }

    const lettersRegexp = /^[A-Za-z]+$/;
    const numberRegexp = /^[0-9]+$/;

    export class LettersOnlyValidator implements StringValidator {
        isAcceptable(s: string) {
            return lettersRegexp.test(s);
        }
    }

    export class ZipCodeValidator implements StringValidator {
        isAcceptable(s: string) {
            return s.length === 5 && numberRegexp.test(s);
        }
    }
}

后续使用的时候,直接去对应命名空间下的验证器即可:

const lettersOnlyValidator = new Validation.LettersOnlyValidator()
lettersOnlyValidator.isAcceptable(123)

命名空间解析

开头我们说过,命名空间即模块。那么我们来思考一下模块的作用是什么呢?隔离作用域,避免污染全局变量?没错,我感觉也是这样,我们可以通过模块机制限制变量、函数或者类的生效范围。这与 JavaScript 中某种用法很像。我们来看看下面这段代码:

var Module;
(function (Module) {
    Module.foo = 123
})(Module || Module = {})

我们可以通过以上方式用来为现有对象设置属性,或者创建一个新对象,然后再为其设置属性。这种用法意味着可以实现多个边界拆成的块。

(function(something) {
  something.foo = 123;
})(something || (something = {}));

console.log(something); // { foo: 123 }

(function(something) {
  something.bar = 456;
})(something || (something = {}));

console.log(something); // { foo: 123, bar: 456 }

在 TypeScript 中,可以使用 namespace 关键字来声明命名空间实现上述的分组。

namespace Utility {
  export const log = (msg: string) => {
    console.log(msg);
  }

  export const error = (msg: string) => {
    console.log(msg);
  }
}

// usage
Utility.log('Call me');
Utility.error('maybe');

上述代码编译成 JavaScript 后的代码如下:

var Utility;
(function (Utility) {
    Utility.log = function (msg) {
        console.log(msg);
    };

    Utility.error = function (msg) {
        console.log(msg);
    };
})(Utility || (Utility = {}));
Utility.log('Call me');
Utility.error('maybe');

看!!!是不是和 JavaScript 的立即执行函数一模一样。

简单总结一下:
namespace 关键字声明的命名空间,主要作用是组织代码,逻辑分组。其最终效果与 JavaScript 的立即执行函数一致,可以实现多个边界拆成的代码块。

好啦!以上便是「 TypeScript 命名空间 」的全部内容,感谢阅读。

欢迎各路大佬讨论、批评、指正,共同进步才是硬道理!