參考
javascript全局變量污染會出現(xiàn)哪些問題简十?
防止js全局變量污染方法總結(jié)-待續(xù)
一、問題
例如护姆,你有兩個.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需要分割數(shù)組内狸,然后兩個作者都寫了個split函數(shù),那肯定有一個模塊要壞掉了
二厘擂、解決辦法
1.定義全局變量命名空間
只創(chuàng)建一個全局變量昆淡,并定義該變量為當前應用容器,把其他全局變量追加在該命名空間下
var MY={};
my.name={
big_name:"zhangsan",
small_name:"lisi"
};
my.work={
school_work:"study",
family_work:"we are"
};
2.利用匿名函數(shù)將腳本包裹起來
(function(){
var exp={};
var name="aa";
exp.method=function(){
return name;
};
window.ex=exp;
})();
三驴党、ts namespace module
TypeScript中的module相當于ActionScript3中的Package
命名空間:主要是為了區(qū)分不同人做的房子瘪撇,以及系統(tǒng)的房子。你的房子可能是這個樣子的他的是另一個樣子港庄,然后都是同一個名字倔既,看起來沒辦法區(qū)分。就像A小區(qū)有一棟樓房叫6#鹏氧,B小區(qū)恰好也有渤涌,我們要去B小區(qū)的6#怎么辦?所以要去的話就要加個前綴把还,我要去B小區(qū)的6#实蓬,這個A小區(qū)和B小區(qū)就是命名空間了
以下參考
TypeScript新手入門
TypeScript Modules(模塊)
在TS中【組織程式碼的方法】
外部模組 - module
模組之間是不同功能,利用import/export來互相引用彼此公開的功能內(nèi)部模組 - namespace
模組之間是相近的功能吊履,使用namespace集中功能安皱。
1.moudle 外部模組
使用 export (用法同ES6)
將想要分享的變數(shù)、函式艇炎、類別及介面做 公開使用 import (用法同ES6)
引用 不同檔案並設定公開的變數(shù)酌伊、函式、類別及介面
MyExport.ts
export class SomeType { /* ... */ }
export function someFn { /* ... */ }
App.ts
import { SomeType,somefn } form './Myexport';
let x = new SomeType();
let y = someFn();
2.namespace 內(nèi)部模組(命名空間)
請先觀察以下寫法缀踪,有什麼缺點居砖。
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的缺點是容易造成名稱衝突
前例的寫法可修改成模組化奏候,關(guān)鍵字使用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公開蔗草。
而變數(shù)HALF是實現(xiàn)的細節(jié),就不必要使用export厚棵,
因此變數(shù)HALF在模組外是不可見的蕉世。
可以觀察到編譯成ES3之後,模組是被包裝成立即函式婆硬,因此避免了全域環(huán)境汙染狠轻。
隨著應用的擴展,我們希望將程式拆分成多個文件.使每個檔案的功能更單純彬犯,更方便維護向楼。(單一職責原則)
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;}
}
}
雖然每個文件是單獨的查吊,但他們都在為同一個模塊貢獻功能,并且在代碼中定義他們的時候就會被調(diào)用湖蜕。因為每個文件是相互依賴的逻卖,我們已經(jīng)添加了"reference"標簽來告訴編譯器文件之間的關(guān)系。
ps:關(guān)于reference,參考TypeScript 三斜線指令,/// <reference path="..." />
指令是三斜線指令中最常見的一種昭抒。 它用于聲明文件間的 依賴评也。三斜線引用告訴編譯器在編譯過程中要引入的額外的文件。當使用--out或--outFile時灭返,它也可以做為調(diào)整輸出內(nèi)容順序的一種方法盗迟。 文件在輸出文件內(nèi)容中的位置與經(jīng)過預處理后的輸入順序一致。
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
一旦有多個文件參與項目熙含,我們得確保所需編譯的代碼是否都已加載罚缕,有兩種方式可以實現(xiàn)。
我們可以使用 -out 將所有的文件內(nèi)容輸出到一個單獨的JavaScript文件中:
tsc --out your.js Test.ts
編譯器會根據(jù)文件中的"reference"標簽自動地將輸出文件進行有序的排序怎静,你也可以指定輸出到單獨的文件:
tsc --out your.js Validation.ts LettersOnlyValidator.ts ZipCodeValidator.ts Test.ts
或者我們也可以對每個文件進行單獨的編譯邮弹。如果產(chǎn)生多個js文件,我們就需要使用<script>標簽用適當?shù)捻樞騺砑虞d文件蚓聘,例如:
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();
總結(jié):
模塊是自聲明的;兩個模塊之間的關(guān)系是通過在文件級別上使用imports和exports建立的夜牡。
3.優(yōu)先使用namespace
以下參考TS1.5 以后导饲,推薦全面使用namespace關(guān)鍵字代替module
大體意思就是 TS1.5 以后,推薦全面使用namespace關(guān)鍵字代替module氯材。因為JS里本身就有module的概念,而且已經(jīng)是ES6標準里的關(guān)鍵字硝岗,各種加載框架比如CommonJS氢哮,AMD等也都有module的概念,但是TS里之前的module關(guān)鍵字與他們都不太相同型檀。所以換了一個關(guān)鍵字加以區(qū)分冗尤,避免造成概念上的混淆。實際語法上胀溺,使用namespace等價于TS以前使用的module裂七,然后推薦代碼中不要再出現(xiàn)module關(guān)鍵字,這個關(guān)鍵字基本上變成了一個編譯后和運行時里的概念仓坞,留給純JS中使用背零。
如果要用一句話解釋TS里的namespace與JS里module的區(qū)別,那主要在于文件上:TS里的namespace是跨文件的无埃,JS里的module是以文件為單位的徙瓶,一個文件一個module毛雇。
TS里的namespace主要是解決命名沖突的問題,會在全局生成一個對象侦镇,定義在namespace內(nèi)部的類都要通過這個對象的屬性訪問灵疮,例如 egret.DisplayObject,egret就是namespace的對象,DisplayObject則是那個類名壳繁。因為是注冊到全局的震捣,所以跨文件也能正常使用,不同的文件能夠讀取其他文件注冊在全局的命名空間內(nèi)的信息闹炉,也可以注冊自己的蒿赢。namespace其實比較像其他面向?qū)ο缶幊陶Z言里包名的概念。
而JS里的module剩胁,主要是解決加載依賴關(guān)系的诉植。跟文件綁定在一起,一個文件就是一個module昵观。在一個文件中訪問另一個文件必須要加載另一個文件晾腔。在NodeJS里是用CommonJS處理模塊加載,因為是運行在本地啊犬,所以可以同步加載灼擂,寫起來也比較方便。用到一個文件就require它一下觉至,作為一個變量剔应。而在Web端的RequireJS使用的是AMD處理模塊加載,是異步的语御。其實就是把所有代碼寫在回調(diào)里峻贮,先去異步加載依賴的所有文件。
所以可以簡單的理解应闯,namespace的概念等同于包名纤控,module的概念等同于文件。
namespace com.data{
export class HashMap {
}
//使用
import HashMap = com.data.HashMap;
class ModuleManager {
...
private moduleMap: HashMap;
constructor() {
this.moduleMap = new HashMap();
}
}
最后碉纺,這個帖子講得非常清楚關(guān)于TypeScript中的module和export關(guān)鍵詞
module大致的意思就是模塊船万, 一個模塊中有若干類,假如我寫了兩個類都叫 A 骨田。那怎么區(qū)分呢耿导,那么就使用這個module關(guān)鍵詞將這兩個類定義在不同模塊就行了。module還有一個作用也是主要作用就是將一些不同特征的的類區(qū)分開态贤。 比如 egret里面有幾大模塊舱呻,核心模塊叫 egret , gui模塊叫 egret.gui悠汽,RES模塊就叫RES 狮荔, dragonBones模塊叫dragonBones胎撇。 這些模塊就是按功能劃分的,一個模塊負責一些特定的功能殖氏。
再比較一下as3中package關(guān)鍵詞與module的不同晚树。as3中一般一個類在哪個文件夾下,那這個類的package就是這個相對于src文件夾的名字雅采,這樣就不用擔心不同文件夾下有名稱相同的類而無法區(qū)分了爵憎。ts中module與類所在的文件夾無關(guān),可能不同文件夾下的類都是一個module婚瓜,一個文件夾下的類是不同module(這種情況最好不要出現(xiàn))宝鼓。 從某種角度來說,module的概念包括了package巴刻。你完全可以把某一個文件夾下的類定義的module定義成相對于src文件夾的名字就和as3的package是一樣的愚铡。不過不推薦這種做法,這樣會書寫不便胡陪,引用每一個類都要加上module名前綴沥寥。
在一個module下的不同類之間的相互調(diào)用不需要加模塊名。比如 egret這個模塊中有很多類但是在egret的源碼中你幾乎看不到egret.XXX這樣的調(diào)用柠座,因為他們都是在一個模塊下邑雅。 同理假如你寫了個類module是egret,那這個類調(diào)用egret里面的類也不需要加egret前綴了妈经。但是不建議這樣做淮野,因為模塊的核心用法就是定義一組相同特征的類。
子模塊定義吹泡。我們可以查看egret中GUI的源碼骤星,發(fā)現(xiàn)GUI中的類module名都是egret.gui。這個gui就是egret的子模塊了爆哑。 在子模塊中調(diào)用父模塊的類也是不需要加前綴的妈踊。比如egret.gui中的類調(diào)用egret中的類是不需要加egret前綴的。 在父模塊中調(diào)用子模塊只需要加上相對于父模塊的模塊名就行了泪漂。比如egret中的類調(diào)用egret.gui中的類使用gui.XXX。
關(guān)于export的用法歪泳。 在使用module時定義一個類需要在前面加上export關(guān)鍵詞萝勤。表示在這個模塊中導入了這個類(在默認模塊下不需要加export)。也可以不加但是不加的話這個類是無法在這個文件外部訪問的呐伞,相當與內(nèi)部類敌卓。另外export還可以用于function。這些用法的一個典型的例子就是RES模塊中伶氢, 我們通常會使用RES.getRes(XXX)來獲取資源趟径,好像這是一個叫RES類的靜態(tài)方法瘪吏,其實不然。搜索下發(fā)現(xiàn)根本就沒有RES這個類蜗巧,RES是模塊名掌眠,getRes是RES模塊下的一個方法。代碼在Resource.ts文件中幕屹,如下:
export function getRes(key:string):any{
return instance.getRes(key);
}
所以export也可以用于導入方法蓝丙。同理 egret.setTimeout這類的方法都是使用export導入的方法 。再來看上述例子中的 instance 實際上是Resource這個類的一個實例望拖,只不過這個類是一個內(nèi)部類渺尘。可以看到在Resource.ts是這樣定義的
class Resource extends egret.EventDispatcher{
}
這里沒用使用export關(guān)鍵詞说敏,這樣外界就無法訪問這個類鸥跟,這個類只在內(nèi)部使用很好的封裝起來了。這是一個很好的用法盔沫。
4.
typescript已經(jīng)有模塊系統(tǒng)了医咨,為什么還需要namespace? - Trotyl Yu的回答 - 知乎