【TypeScript】深入学习TypeScript命名空间

TypeScript学习:TypeScript从入门到精通

蓝桥杯真题解析:蓝桥杯Web国赛真题解析

个人简介:即将大三的学生,热爱前端,热爱生活
你的一键三连是我更新的最大动力❤️!


前言

最近博主一直在创作TypeScript的内容,所有的TypeScript文章都在我的TypeScript从入门到精通专栏里,每一篇文章都是精心打磨的优质好文,并且非常的全面和细致,期待你的订阅❤️

在【TypeScript】深入学习TypeScript模块化文章的最后我们提到了TypeScript的命名空间namespaces,这一节我们就将深入去学习namespaces,并探讨它与模块的区别

文章目录

  • 前言
  • 1、空间声明
  • 2、空间合并
  • 3、实现原理
  • 4、模块化空间
  • 5、空间别名
  • 6、命名空间与模块
  • 结语

1、空间声明

在代码量较大的情况下,为了避免各种变量命名的冲突,可将相似功能的函数、类、接口等放置到命名空间之中

TypeScript的命名空间使用namespaces声明,它可以将代码包裹起来,并可以使用export选择性的向外暴露指定内容:

namespace Ailjx {
    // a没有使用export向外暴露,在外部无法访问
    let a;
    export const str = "Ailjx";
    export type S = string;
    export function f() {}
    export class N {}
}

这里定义了一个名为Ailjx的命名空间,在外部可以使用Ailjx.的形式访问其内部通过export暴露的成员:

const s: Ailjx.S = Ailjx.str;
Ailjx.f();
new Ailjx.N();
// 类型“typeof Ailjx”上不存在属性“a”
// console.log(Ailjx.a);// err

从上面可以看出TypeScript的命名空间实际上就像一层大的容器,将内容包裹在其中,将其私有化,这就避免了外部其它变量与其内容命名冲突的问题

2、空间合并

多个相同名称的命名空间会自动进行合并,这就使得命名空间可以访问或修改同一名称下其它空间export的成员:

namespace Ailjx {
    export let a = 1;
}
namespace Ailjx {
    a = 2;
    export let b = 3;
}

console.log(Ailjx.a, Ailjx.b); // 2 3

没有export的成员只在当前命名空间有效,不会受合并的影响:

namespace Ailjx {
    // s没有export,它只在当前空间有效
    let s = 0;
}
namespace Ailjx {
    // 访问不到上个空间的s
    s = 1; //❌❌❌err:找不到名称“s”
}

同一名称下的不同空间可以有相同名称的非export成员,如下面的变量s

namespace A {
    // s没有export,它只在当前空间有效
    let s = 0;
    export function getS1() {
        console.log(s);
    }
}
namespace A {
    // s没有export,它只在当前空间有效
    let s = 1;
    export function getS2() {
        console.log(s);
    }
}

A.getS1(); // 0
A.getS2(); // 1

从这可以看出TypeScript相同命名的空间并不只是简单的合并,这与闭包有些相似,然而当你查看上方代码编译后的js文件,你就会发现TypeScript的命名空间就是以闭包的形式实现的,见下方实现原理:

3、实现原理

上面命名空间A编译后的js代码:

"use strict";
"use strict";
var A;
(function (A) {
    // s没有export,它只在当前空间有效
    let s = 0;
    function getS1() {
        console.log(s);
    }
    A.getS1 = getS1;
})(A || (A = {}));
(function (A) {
    // s没有export,它只在当前空间有效
    let s = 1;
    function getS2() {
        console.log(s);
    }
    A.getS2 = getS2;
})(A || (A = {}));
A.getS1(); // 0
A.getS2(); // 1

再看一个export暴露成员的命名空间:

namespace B {
    export let s = 0;
}
namespace B {
    s = 1;
}

编译后的js

"use strict";
var B;
(function (B) {
    B.s = 0;
})(B || (B = {}));
(function (B) {
    B.s = 1;
})(B || (B = {}));

有一定经验的大佬看到编译后的js代码后,应该一下就能理解TypeScript命名空间的实现原理

原理解读:

  • 每一个命名空间的名称在js中就是一个全局变量(相同名称的空间用的是同一个变量,我将该变量称为名称变量,如上方的var A; var B;,名称变量实际就是一个存储export内容的对象)

  • 每一个命名空间在js中都是一个传入其对应名称变量立即执行函数

  • 命名空间内通过export暴露的内容在js中会挂载到其对应的名称变量中,这也就是同一名称不同空间的命名空间能够相互访问其内部export成员的原因(因为它们接受的是同一个名称变量)

  • 命名空间内非export暴露的内容在js中不会挂载到其对应的名称变量中,而只是在其立即执行函数中声明,并只对当前函数空间生效

4、模块化空间

命名空间结合TypeScript模块化,可以将其抽离到一个单独的ts文件内,变成模块化的空间:

// src/a.ts
export namespace A {
    export let s = 99;
}

引入并使用命名空间:

// src/hello.ts
import { A } from "./a";
console.log(A.s); // 99

5、空间别名

使用 import q = x.y.z 来为常用对象创建更短的名称:

namespace A {
    export namespace B {
        export class C {
            constructor() {
                console.log(999);
            }
        }
    }
}

import MyC = A.B.C;
new MyC(); // 999  与new A.B.C()等价
new A.B.C(); // 999

没想到import语法还能这样用,虽然很奇怪,但这在一些场景下应该会很实用

从这里也可以看出命名空间是可以嵌套使用

6、命名空间与模块

命名空间: 相当于内部模块,主要用于组织代码,避免命名冲突

命名空间是一种特定于 TypeScript 的代码组织方式,它只是全局命名空间中命名的 JavaScript 对象,这使得命名空间成为一个非常简单易用的构造

就像所有全局命名空间污染一样,使用它很难识别组件依赖关系,尤其是在大型应用程序中

模块: 外部模块的简称,侧重代码的复用,一个模块里能够包含多个命名空间

模块可以包含代码和声明,依赖于模块加载器(如CommonJs/Require.js)或支持ES模块的运行,模块提供了更好的代码重用,更强的隔离性和更好的捆绑工具支持

同样值得注意的是,对于Node.js应用程序,模块是默认的,官方在现代代码中推荐模块而不是命名空间

EC6开始,模块是语言的原生部分,所有兼容的引擎实现都应该支持,因此,对于新项目,模块将是推荐的代码组织机制

结语

至此,TypeScript命名空间的内容就全部结束了,关注博主下篇更精彩!

博主的TypeScript从入门到精通专栏正在慢慢的补充之中,赶快关注订阅,与博主一起进步吧!期待你的三连支持。

参考资料:TypeScript官网

如果本篇文章对你有所帮助,还请客官一件四连!❤️

你可能感兴趣的:(typescript,学习,javascript)