TS 筆記十 namespace module

參考
javascript全局變量污染會出現哪些問題?
防止js全局變量污染方法總結-待續

一、問題

例如,你有兩個.js。

1.js:

function f() {
    alert("f() in 1.js");
}

setTimeout(function() {
    f();
}, 1000);
2.js:

function f() {
    alert("f() in 2.js");
}

setTimeout(function() {
    f();
}, 2000);

如果你在html中先載入1.js,再載入2.js,那么你就會看到兩次"f() in 2.js"。因為后載入的2.js把f重新定義了。要比較實際的例子的話,可以想像1.js需要分割字符串,2.js需要分割數組,然后兩個作者都寫了個split函數,那肯定有一個模塊要壞掉了

二、解決辦法
1.定義全局變量命名空間

只創建一個全局變量,并定義該變量為當前應用容器,把其他全局變量追加在該命名空間下

var MY={};
        my.name={
            big_name:"zhangsan",
            small_name:"lisi"
        };
        my.work={
            school_work:"study",
            family_work:"we are"
       };
2.利用匿名函數將腳本包裹起來
(function(){
    var exp={};
    var name="aa";
    exp.method=function(){
        return name;
    };
    window.ex=exp;
})();
三、ts namespace module

TypeScript中的module相當于ActionScript3中的Package
命名空間:主要是為了區分不同人做的房子,以及系統的房子。你的房子可能是這個樣子的他的是另一個樣子,然后都是同一個名字,看起來沒辦法區分。就像A小區有一棟樓房叫6#,B小區恰好也有,我們要去B小區的6#怎么辦?所以要去的話就要加個前綴,我要去B小區的6#,這個A小區和B小區就是命名空間了

以下參考
TypeScript新手入門
TypeScript Modules(模塊)
在TS中【組織程式碼的方法】

  • 外部模組 - module
    模組之間是不同功能,利用import/export來互相引用彼此公開的功能

  • 內部模組 - namespace
    模組之間是相近的功能,使用namespace集中功能。

1.moudle 外部模組
  • 使用 export (用法同ES6)
    將想要分享的變數、函式、類別及介面做 公開

  • 使用 import (用法同ES6)
    引用 不同檔案並設定公開的變數、函式、類別及介面

MyExport.ts
    export class SomeType  { /* ... */ }
    export function someFn { /* ... */ }
App.ts
    import { SomeType,somefn } form './Myexport';
    let x = new SomeType();
    let y = someFn();
2.namespace 內部模組(命名空間)

請先觀察以下寫法,有什麼缺點。

interface Shape {
    area(h:number,w:number):number;
}

class Square implements Shape {
    area(h:number,w:number) {return h*w;}
}

class Triangle implements Shape { 
    area(h:number,w:number) {return (h*w) / 2;}
}

let s = new Square();
console.log(s.area(10,5)); // 50
let t = new Triangle();
console.log(t.area(10,5)); // 25
Shape、Square、Triangle 放在 global namespace

放在global namespace的缺點是容易造成名稱衝突
前例的寫法可修改成模組化,關鍵字使用namespace

namespace Geometric { 
    const HALF = 0.5;
    
    export interface Shape {
        area(h:number,w:number):number;
    } 
    
    export class Square implements Shape {
        area(h:number,w:number) {return h*w;}
    }

    export class Triangle implements Shape { 
        area(h:number,w:number) { return (h*w)*HALF };
    }
} //所以global namespace,只有Geometric這個物件

let s = new Geometric.Square();
console.log(s.area(10,5)); // 50
let t = new Geometric.Triangle();
console.log(t.area(10,5)); // 25

我們希望介面跟類別是公開的,所以使用export公開。
而變數HALF是實現的細節,就不必要使用export,
因此變數HALF在模組外是不可見的。

image.png

可以觀察到編譯成ES3之後,模組是被包裝成立即函式,因此避免了全域環境汙染。

隨著應用的擴展,我們希望將程式拆分成多個文件.使每個檔案的功能更單純,更方便維護。(單一職責原則)

Shape.ts

namespace Geometric {
    export interface Shape {
        area(h:number,w:number):number;
    } 
}
Square.ts

/// <reference path="Shape.ts" />
namespace Geometric {
    export class Square implements Shape {
        area(h:number,w:number) {return h*w;}
    }
}
Triangle.ts

/// <reference path="Shape.ts" />
namespace Geometric {
    export class Triangle implements Shape {
        area(h:number,w:number) {return h*w;}
    }
}

雖然每個文件是單獨的,但他們都在為同一個模塊貢獻功能,并且在代碼中定義他們的時候就會被調用。因為每個文件是相互依賴的,我們已經添加了"reference"標簽來告訴編譯器文件之間的關系。

ps:關于reference,參考TypeScript 三斜線指令,/// <reference path="..." />指令是三斜線指令中最常見的一種。 它用于聲明文件間的 依賴。三斜線引用告訴編譯器在編譯過程中要引入的額外的文件。當使用--out或--outFile時,它也可以做為調整輸出內容順序的一種方法。 文件在輸出文件內容中的位置與經過預處理后的輸入順序一致。

App.ts

/// <reference path="Shape.ts" />
/// <reference path="Square.ts" />
/// <reference path="Triangle.ts" />

let s = new Geometric.Square();
console.log(s.area(10,5)); // 50
let t = new Geometric.Triangle();
console.log(t.area(10,5)); // 25

一旦有多個文件參與項目,我們得確保所需編譯的代碼是否都已加載,有兩種方式可以實現。

我們可以使用 -out 將所有的文件內容輸出到一個單獨的JavaScript文件中:
tsc --out your.js Test.ts
編譯器會根據文件中的"reference"標簽自動地將輸出文件進行有序的排序,你也可以指定輸出到單獨的文件:

tsc --out your.js Validation.ts LettersOnlyValidator.ts ZipCodeValidator.ts Test.ts

或者我們也可以對每個文件進行單獨的編譯。如果產生多個js文件,我們就需要使用<script>標簽用適當的順序來加載文件,例如:

MyTestPage.html (文件引用)

<script src="Validation.js" type="text/javascript"></script />
<script src="LettersOnlyValidator.js" type="text/javascript"></script />
<script src="ZipCodeValidator.js" type="text/javascript"></script />
<script src="Test.js" type="text/javascript"></script />

別名

當取用模組的path比較長時,可以使用 import q = x.y.z 的語法.給常用的模組起一個簡短的名稱

    namespace Shapes {
        export namespace Polygons {
            export class Square {}
            export class Triangle {}
        }
    }
    
    //沒有用別名之前
    var test1 = new Shapes.Polygons.Square();
    var test2 = new Shapes.Polygons.Triangle();
    
    //使用別名之後
    import pg = Shapes.Polygons;
    var sq = new pg.Square();
    var tri = new pg.Triangle();

總結:
模塊是自聲明的;兩個模塊之間的關系是通過在文件級別上使用imports和exports建立的。

3.優先使用namespace

以下參考TS1.5 以后,推薦全面使用namespace關鍵字代替module

大體意思就是 TS1.5 以后,推薦全面使用namespace關鍵字代替module。因為JS里本身就有module的概念,而且已經是ES6標準里的關鍵字,各種加載框架比如CommonJS,AMD等也都有module的概念,但是TS里之前的module關鍵字與他們都不太相同。所以換了一個關鍵字加以區分,避免造成概念上的混淆。實際語法上,使用namespace等價于TS以前使用的module,然后推薦代碼中不要再出現module關鍵字,這個關鍵字基本上變成了一個編譯后和運行時里的概念,留給純JS中使用。

如果要用一句話解釋TS里的namespace與JS里module的區別,那主要在于文件上:TS里的namespace是跨文件的,JS里的module是以文件為單位的,一個文件一個module。

TS里的namespace主要是解決命名沖突的問題,會在全局生成一個對象,定義在namespace內部的類都要通過這個對象的屬性訪問,例如 egret.DisplayObject,egret就是namespace的對象,DisplayObject則是那個類名。因為是注冊到全局的,所以跨文件也能正常使用,不同的文件能夠讀取其他文件注冊在全局的命名空間內的信息,也可以注冊自己的。namespace其實比較像其他面向對象編程語言里包名的概念。

而JS里的module,主要是解決加載依賴關系的。跟文件綁定在一起,一個文件就是一個module。在一個文件中訪問另一個文件必須要加載另一個文件。在NodeJS里是用CommonJS處理模塊加載,因為是運行在本地,所以可以同步加載,寫起來也比較方便。用到一個文件就require它一下,作為一個變量。而在Web端的RequireJS使用的是AMD處理模塊加載,是異步的。其實就是把所有代碼寫在回調里,先去異步加載依賴的所有文件。

所以可以簡單的理解,namespace的概念等同于包名,module的概念等同于文件。

namespace com.data{
export class HashMap {
}

//使用
import HashMap = com.data.HashMap;
class ModuleManager {
...
private moduleMap: HashMap;
    constructor() {
        this.moduleMap = new HashMap();
    }
}

最后,這個帖子講得非常清楚關于TypeScript中的module和export關鍵詞

module大致的意思就是模塊, 一個模塊中有若干類,假如我寫了兩個類都叫 A 。那怎么區分呢,那么就使用這個module關鍵詞將這兩個類定義在不同模塊就行了。module還有一個作用也是主要作用就是將一些不同特征的的類區分開。 比如 egret里面有幾大模塊,核心模塊叫 egret , gui模塊叫 egret.gui,RES模塊就叫RES , dragonBones模塊叫dragonBones。 這些模塊就是按功能劃分的,一個模塊負責一些特定的功能。

再比較一下as3中package關鍵詞與module的不同。as3中一般一個類在哪個文件夾下,那這個類的package就是這個相對于src文件夾的名字,這樣就不用擔心不同文件夾下有名稱相同的類而無法區分了。ts中module與類所在的文件夾無關,可能不同文件夾下的類都是一個module,一個文件夾下的類是不同module(這種情況最好不要出現)。 從某種角度來說,module的概念包括了package。你完全可以把某一個文件夾下的類定義的module定義成相對于src文件夾的名字就和as3的package是一樣的。不過不推薦這種做法,這樣會書寫不便,引用每一個類都要加上module名前綴。

在一個module下的不同類之間的相互調用不需要加模塊名。比如 egret這個模塊中有很多類但是在egret的源碼中你幾乎看不到egret.XXX這樣的調用,因為他們都是在一個模塊下。 同理假如你寫了個類module是egret,那這個類調用egret里面的類也不需要加egret前綴了。但是不建議這樣做,因為模塊的核心用法就是定義一組相同特征的類。

子模塊定義。我們可以查看egret中GUI的源碼,發現GUI中的類module名都是egret.gui。這個gui就是egret的子模塊了。 在子模塊中調用父模塊的類也是不需要加前綴的。比如egret.gui中的類調用egret中的類是不需要加egret前綴的。 在父模塊中調用子模塊只需要加上相對于父模塊的模塊名就行了。比如egret中的類調用egret.gui中的類使用gui.XXX。

關于export的用法。 在使用module時定義一個類需要在前面加上export關鍵詞。表示在這個模塊中導入了這個類(在默認模塊下不需要加export)。也可以不加但是不加的話這個類是無法在這個文件外部訪問的,相當與內部類。另外export還可以用于function。這些用法的一個典型的例子就是RES模塊中, 我們通常會使用RES.getRes(XXX)來獲取資源,好像這是一個叫RES類的靜態方法,其實不然。搜索下發現根本就沒有RES這個類,RES是模塊名,getRes是RES模塊下的一個方法。代碼在Resource.ts文件中,如下:

    export function getRes(key:string):any{
        return instance.getRes(key);
    }

所以export也可以用于導入方法。同理 egret.setTimeout這類的方法都是使用export導入的方法 。再來看上述例子中的 instance 實際上是Resource這個類的一個實例,只不過這個類是一個內部類。可以看到在Resource.ts是這樣定義的

class Resource extends egret.EventDispatcher{
}

這里沒用使用export關鍵詞,這樣外界就無法訪問這個類,這個類只在內部使用很好的封裝起來了。這是一個很好的用法。

4.

typescript已經有模塊系統了,為什么還需要namespace? - Trotyl Yu的回答 - 知乎

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,182評論 6 543
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,489評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,290評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,776評論 1 317
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,510評論 6 412
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,866評論 1 328
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,860評論 3 447
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 43,036評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,585評論 1 336
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,331評論 3 358
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,536評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,058評論 5 363
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,754評論 3 349
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,154評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,469評論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,273評論 3 399
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,505評論 2 379

推薦閱讀更多精彩內容