小記 TypeScript 中的循環引用問題


轉載至: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 的定義導出都需要及時訪問導入模塊的導出數據),我們只能通過改變模塊的導入順序來規避導入出錯的問題 …


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM