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阅读 212,222评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,455评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,720评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,568评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,696评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,879评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,028评论 3 409
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,773评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,220评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,550评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,697评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,360评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,002评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,782评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,010评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,433评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,587评论 2 350

推荐阅读更多精彩内容