標籤:

typescript已經有模塊系統了,為什麼還需要namespace?

模塊功能,跟namespace功能上是不是有點重複,namespace有什麼具體的應用場景嗎


TL; DR:

  • Namespace 本來就是 TypeScript 的模塊系統;
  • TypeScript 對 Namespace 的支持比對 ES Module 的支持還要早得多(不算改名)。

---

題主理解的時間線並不正確,TypeScript 的出現遠早於 ES2015 定稿。而 ES 規範中的標準化模塊系統是從 ES 2015 中才開始出現的(顯然也不是從 TypeScript 里抄的)。

大體的時間表:

  • 1999/11: ES 3 發布;
  • 2008/08: ES 4 被廢棄;
  • 2009/11: ES 3.1 改名 ES 5 發布;
  • 2011/06: ES 5.1 發布;
  • 2012/10: TypeScript 0.8 發布;
  • 2015/06: ES 2015 發布;
  • ...

TypeScript 問世的那個時候,JavaScript 還是根本沒有一個光明的前途的。與 CoffeeScript 類似,TypeScript 也試圖引領 JavaScript 的未來,不過方式並不相同。TypeScript 並不打算創立一門新的語言,而是在 JavaScript 之上直接擴展,從而成為未來的 JavaScript。

作為一個未來的 JavaScript,當然會不可避免地去完善 JavaScript 所不足的地方,例如模塊系統。

早期的 TypeScript 具備兩套模塊系統,分別叫做 Internal Module 和 External Module。(雖然這個命名方式非常的俗氣)

當時對於像 CommonJS 這樣基於文件的模塊系統和 AMD、AngularJS 這樣基於作用域的模塊系統誰更優越似乎還沒有確定的結論。

External Module 為單文件單模塊(類似於 CommonJS 或者 ES Module),語法為:

// foo.js
export = foo

// bar.js
import foo = require(foo)
console.log(foo)
export = bar

看起來 External Module 有寫類似於 CommonJS,不過 require 的 assignee 並不是 var(記住當時沒有 ES 2015,不要說應該 const),而是 import。而導出也並非 module.exports,而是 export

另外存在一個 Internal Module,與文件劃分無關,只是單純地隔離作用域,語法為:

// scripts.js
module foo {
export var a = 1
export function add(x: number, y: number): number {
return x + y
}
}

module bar {
var b = foo.a + foo.add(1, 2)
}

由於引入了專門的語法,不需要像 AMD 那樣依靠函數作用域來隔離代碼塊,顯得更為簡潔。

隨著 ES2015 出現(以及隨之而來的規範演進機制),TypeScript 的定位也發生了轉變,不再提供 ES 規範中沒有的功能,轉而專註於為 ES 代碼提供靜態類型檢查。

所以必然地,TypeScript 放棄了自身的模塊系統,轉而使用 ES 規範中的模塊系統,相應的 Tracker 有:

  • Support ES6 import and export declarations by ahejlsberg · Pull Request #1983 · Microsoft/TypeScript
  • Complete support for ES6 modules by ahejlsberg · Pull Request #2197 · Microsoft/TypeScript
  • Revised ES6 modules by ahejlsberg · Pull Request #2460 · Microsoft/TypeScript
  • ES6 Modules · Issue #2242 · Microsoft/TypeScript

第三個 PR 中,詳細闡述了原 TypeScript External Module 與新 ES Module 的對應關係(然而有些方面並不能很好地對應起來)。最後一個 Issue 的後續討論中也有很多關於原 Internal Module 與新 ES Module 的相關內容。

當然原有的 External Module 語法依然能夠繼續使用,只是文檔中幾乎不再提及。

隨著 ES Module 成為 TypeScript 的標準模塊系統,原有的 Internal Module 和 External Module 概念容易產生不必要的誤解,因此 Internal Module 改名為 Namespace,而 Module 就專門指代 ES Module。相應的 Tracker 在:

  • Namespace keyword · Issue #2159 · Microsoft/TypeScript
  • Namespaces by ahejlsberg · Pull Request #2923 · Microsoft/TypeScript

TypeScript 是一個對兼容性及其重視的項目,所以仍然保留了這些舊方案的可用性,但是在實際項目中應當儘可能避免使用。

註:在一些非 TypeScript 原生代碼的 .d.ts 文件中可能仍然存在對原有 External Module 和 Internal Module/Namespace 的使用,主要是由於 ES Module 過於靜態,對 JavaScript 代碼結構的表達能力有限。


因為ts編譯器自己的代碼大量使用了namespace關鍵字蛤,我的項目里也有很多,感覺看起來比module順眼多了。

namespace編譯成最原始的閉包代碼,技術原理最簡單,兼容性也最好,特別適合把所有代碼全部打包在一起的項目(順序合併),不用每個文件寫一堆import,時間都浪費了,不好的地方就是哪個命名空間代碼是放在哪個文件的沒有強制,會比較混亂,並且載入順序有要求,搞不清誰依賴了誰。

——————-

睡不著再答一波,這真是個好問題;其實namespace關鍵字來自.net,因為ts之父也是c#作者,相對於java的package就是兩種派系了,namespace不限定相同名稱空間的代碼放到不同文件里,屬於一种放盪豪邁的風格,我不關注方法和類來自哪個文件以及什麼時候載入了,只要記住名稱空間想用就用,基本不需要寫引用(只要寫很少的引用以及載入大的js或dll),而標準派的做法是讓你清楚明白模塊來自哪裡,用多少引用多少,需要大量的篇幅去維護這些引用,現在我的項目就是平均一個文件有三四十行的import的代碼,寫起來很不舒暢。

放到js的開發環境下,比如ts開源項目的tsc編譯器,最終是編譯(合併)成一個大的js文件,所以根本沒必要用es標準的模塊,並且項目有幾百個文件,如果都用標準模塊去設計,一個文件平均下來得寫50-100行的import,並且要設計很多的包索引文件去export子模塊;這種情況下ns方案顯然更人性化,反正維護該項目的都是老熟人。

以上就是namespace保留的價值,說到底其實就是js的原始閉包,不關注代碼是同步還是非同步載入的,只關注使用體驗。


推薦閱讀:

有趣也有用的現代類型系統
TypeScript入門
Typescript玩轉設計模式 之 對象行為型模式(下)
TypeScript 2.1中的類型運算
vscode編輯器打開大項目能夠快速預覽,這是如何做到的?軟體演算法比atom做的好?

TAG:TypeScript |