小记 TypeScript 中的循环引用问题

摘要:
因此,程序直接返回当前导入结果(尽管当前结果不完整),将类型B添加到模块B的导出数据(exportclassB),完成模块B的导入,继续模块A的导入,将类型A添加到模块A的导出数据中(exportclassA),并完成模块A的输入,尝试访问当前(A模块)导入结果中的类型A定义,但当前(A)导入结果没有类型A的定义(因为A模块的当前导入尚未达到exportclassA)Ops,

转载至:https://blog.csdn.net/tkokof1/article/details/108984865

平时编写 TypeScript 代码时,一般都倾向于使用模块(Module),通过结合使用 import 和 export 我们便可以方便的进行模块的导入和导出.

举个简单的例子,假设我们有以下的 TypeScript 代码文件(A.ts):

export class A {
// methods here
}


可以看到,上述代码使用 export 导出了类型 A,如果我们需要在另外的 TypeScript 代码文件(B.ts)中使用类型 A,我们可以直接使用 import :

import { A } from "./A.ts"

class B {
// use A here
}

接着我们让代码变的复杂一些,假设现在类型 A 也要使用类型 B 了,那么相关的代码可能会变成这样:

import { B } from "./B.ts"

export class A {
// use B here
}
import { A } from "./A.ts"

export class B {
// use A here
}

此时,类型 A 与 类型 B 便产生了循环引用,一般来讲是应该尽量避免的,但是在较大型的项目中往往又很难规避,所以我们需要一种可以处理循环引用问题的方法(之前关于这个话题自己也写过一篇博文),而实际上,TypeScript 中的 import 和 export 是可以处理循环引用的:

当 import 遇到导入完毕或者说正在导入的模块(文件)时,是直接返回导入结果的(尽管这个结果可能是不完整的),而不是递归的进行模块的导入操作,还是拿上面的代码举例,假设我们首先导入 A 模块:

A 模块尝试导入 B 模块
由于 B 模块尚未导入,程序开始导入 B 模块
B 模块尝试导入 A 模块
由于 A 模块正在导入,所以程序直接返回当前导入结果(尽管当前结果是不完整的)
将类型 B 加入到 B 模块的导出数据中(export class B)
B 模块导入完成,继续 A 模块的导入
将类型 A 加入到 A 模块的导出数据中(export class A)
A 模块导入完成
值得注意的是,上述的这种循环引用处理方式是不完备的,该方式并不能正确处理更复杂一些的循环引用情况(主要是在一些需要及时访问模块导出数据的情况下,譬如类继承(extends),静态引用等等)

考虑下面的循环引用情况:

import { C } from "./C.ts"

export class A {
// use C here
}
import { A } from "./A.ts"

export class B extends A {
// methods here
}
import { B } from "./B.ts"

export class C extends B {
// methods here
}

假设我们首先导入 A.ts,我们来分析下导入流程:

A 模块尝试导入 C 模块
由于 C 模块尚未导入,所以我们开始导入 C 模块
C 模块尝试导入 B 模块
由于 B 模块尚未导入,所以我们开始导入 B 模块
B 模块尝试导入 A 模块
由于 A 模块正在导入,所以程序直接返回当前导入结果
B 模块继承 A 模块,尝试在当前(A 模块)导入结果中访问类型 A 的定义
但是当前(A 模块)导入结果中并没有类型 A 的定义(因为当前 A 模块的导入还没有进行到 export class A)
Ops,导入出错(找不到类型 A 的定义) …
对于上面这种情况,其实有一个技巧可以解决上面的问题:在不需要及时访问模块导出数据的情况下,我们可以将模块的导入操作后置.

就上面的例子来讲,我们可以这么修改代码:

export class A {
// use C here
}

// put import after export
import { C } from "./C.ts"
import { A } from "./A.ts"

export class B extends A {
// methods here
}
import { B } from "./B.ts"

export class C extends B {
// methods here
}

我们再来分析下上面代码的导入流程(仍然假设首先导入 A.ts):

A 模块将类型 A 加入到 A 模块的导出数据中(export class A)
A 模块尝试导入 C 模块
由于 C 模块尚未导入,所以我们开始导入 C 模块
C 模块尝试导入 B 模块
由于 B 模块尚未导入,所以我们开始导入 B 模块
B 模块尝试导入 A 模块
由于 A 模块正在导入,所以程序直接返回当前导入结果
类型 B 继承 类型 A ,尝试在当前(A 模块)导入结果中访问类型 A 的定义
当前(A 模块)导入结果中存在类型 A 的定义, 类型 B 可以正常定义导出
B 模块将类型 B 加入到 B 模块的导出数据中(export class B)
B 模块导入完成,继续 C 模块的导入
类型 C 继承 类型 B,尝试在当前(B 模块)导入结果中访问类型 B 的定义
当前(B 模块)导入结果中存在类型 B 的定义, 类型 C 可以正常定义导出
C 模块导入完成, 继续 A 模块的导入
A 模块导入完成
但是如果我们尝试首先导入 B 模块(B.ts)的话,仍然会遇到导入出错的问题:

B 模块尝试导入 A 模块
由于 A 模块尚未导入,所以我们开始导入 A 模块
A 模块尝试导入 C 模块
由于 C 模块尚未导入,所以我们开始导入 C 模块
C 模块尝试导入 B 模块
由于 B 模块正在导入,所以程序直接返回当前导入结果
类型 C 继承 类型 B,尝试在当前(B 模块)导入结果中访问类型 B 的定义
但是当前(B 模块)导入结果中并没有类型 B 的定义(因为当前 B 模块的导入还没有进行到 export class B)
Ops,导入出错(找不到类型 B 的定义) …
这种情况下,我们已经不能通过后置 import 来解决问题了(因为类型 B 和 类型 C 的定义导出都需要及时访问导入模块的导出数据),我们只能通过改变模块的导入顺序来规避导入出错的问题 …

免责声明:文章转载自《小记 TypeScript 中的循环引用问题》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇Winform 多个窗口编辑同一条数据同步的实现linux --> 获取系统启动时间下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

Chrome开发者之测试应用

一、Chrome开发者工具简介 1.1、开发者工具(DevTools)调用 二、Chrome DevTools详细介绍 1.1、模块介绍 2.1、元素(Elements)详解 2.1.1、元素编辑 2.1.2、辅助元素定位 2.1.3、ChroPath插件 3.1、控制台(Console)详解 4.1、源代码(Sources)详解 5.1、网络(Netwo...

Java10 新特性

一、JDK10 发布   2018年3月21日, Oracle官方宣布Java10正式发布。   需要注意的是 Java 9 和 Java 10 都不是 LTS (Long-Term-Support) 版本。和过去的 Java 大版本升级不同,这两个只有半年左右的开发和维护期。而未来的 Java 11,也就是 18.9 LTS,才是 Java 8 之后第一个...

3月18日 winform页面设置与打印

1.页面设置(PageSetupDialog) pageSetupDialog1.Document = printDocument1;    //必须设置打印对象,要不然不知道打印谁    pageSetupDialog1.ShowDialog();                    //ShowDialog是打印这一栏里都有的方法 执行结果: 2...

深入分析Java反射(一)-核心类库和方法

前提 Java反射的API在JavaSE1.7的时候已经基本完善,但是本文编写的时候使用的是Oracle JDK11,因为JDK11对于sun包下的源码也上传了,可以直接通过IDE查看对应的源码和进行Debug。 本文主要介绍反射的基本概念以及核心类Class、Constructor、Method、Field、Parameter的常用方法。 本文极长,请准...

框架模块设计经验总结

转自:http://www.cnblogs.com/zgynhqf/archive/2011/07/15/2107593.html 这是原创,尊重原创、、、、、、、、、、、、     框架模块设计经验总结      三个月没写日志了,比较懒散……下半年准备做OEA 的 B/S 版本,比较复杂,需要从架构设计开始认真入手。正好今天到了部门反思的时间,今天...

TensorFlow 2.0 深度学习实战 —— 浅谈卷积神经网络 CNN

前言 上一章为大家介绍过深度学习的基础和多层感知机 MLP 的应用,本章开始将深入讲解卷积神经网络的实用场景。卷积神经网络 CNN(Convolutional Neural Networks,ConvNet)是一种特殊的深度学习神经网络,近年来在物体识别、图像重绘、视频分析等多个层面得到了广泛的应用。本文将以VGG16预训练模型为例子,从人脸识别、预训练模...