【TypeScript】TypeScript 學習 4——模塊


前端數據驗證在改善用戶體驗上有很大作用,在學了之前的知識的時候,我們很可能會寫出以下代碼:

interface StringValidator {
    isAcceptable(s: string): boolean;
}

var lettersRegexp = /^[A-Za-z]+$/;
var numberRegexp = /^[0-9]+$/;

class LettersOnlyValidator implements StringValidator {
    isAcceptable(s: string) {
        return lettersRegexp.test(s);
    }
}

class ZipCodeValidator implements StringValidator {
    isAcceptable(s: string) {
        return s.length === 5 && numberRegexp.test(s);
    }
}

// Some samples to try
var strings = ['Hello', '98052', '101'];
// Validators to use
var validators: { [s: string]: StringValidator; } = {};
validators['ZIP code'] = new ZipCodeValidator();
validators['Letters only'] = new LettersOnlyValidator();
// Show whether each string passed each validator
strings.forEach(s => {
    for (var name in validators) {
        console.log('"' + s + '" ' + (validators[name].isAcceptable(s) ? ' matches ' : ' does not match ') + name);
    }
});

那么這段代碼最大的問題是什么呢?一個是沒法復用,驗證的封裝和驗證過程在同一個文件,驗證的封裝已經是可以復用的。另一個是接口和兩個實現的類都直接掛接在全局變量上,假如數量一多的話,將會污染整個全局變量。

模塊化就是為了解決這一問題而誕生的。

module Validation {
    export interface StringValidator {
        isAcceptable(s: string): boolean;
    }

    var lettersRegexp = /^[A-Za-z]+$/;
    var numberRegexp = /^[0-9]+$/;

    export class LettersOnlyValidator implements StringValidator {
        isAcceptable(s: string) {
            return lettersRegexp.test(s);
        }
    }

    export class ZipCodeValidator implements StringValidator {
        isAcceptable(s: string) {
            return s.length === 5 && numberRegexp.test(s);
        }
    }
}

// Some samples to try
var strings = ['Hello', '98052', '101'];
// Validators to use
var validators: { [s: string]: Validation.StringValidator; } = {};
validators['ZIP code'] = new Validation.ZipCodeValidator();
validators['Letters only'] = new Validation.LettersOnlyValidator();
// Show whether each string passed each validator
strings.forEach(s => {
    for (var name in validators) {
        console.log('"' + s + '" ' + (validators[name].isAcceptable(s) ? ' matches ' : ' does not match ') + name);
    }
});

我們使用 module 關鍵字來定義模塊,用 export 關鍵字讓我們的接口、類等成員對模塊外可見。

這樣,就只有模塊名掛接在全局變量上,最大限度地避免污染全局變量了。

  • 分隔模塊到多個文件

隨着我們項目的擴展,我們的代碼總不可能只寫在一個文件里。為了更好地維護項目,我們會將特定功能放到一個文件里,然后加載多個功能實現我們想需要的功能。現在我們先將上面的代碼分割到多個文件里。

Validation.ts

module Validation {
    export interface StringValidator {
        isAcceptable(s: string): boolean;
    }
}

LettersOnlyValidator.ts

/// <reference path="Validation.ts" />
module Validation {
    var lettersRegexp = /^[A-Za-z]+$/;
    export class LettersOnlyValidator implements StringValidator {
        isAcceptable(s: string) {
            return lettersRegexp.test(s);
        }
    }
}

ZipCodeValidator.ts

/// <reference path="Validation.ts" />
module Validation {
    var numberRegexp = /^[0-9]+$/;
    export class ZipCodeValidator implements StringValidator {
        isAcceptable(s: string) {
            return s.length === 5 && numberRegexp.test(s);
        }
    }
}

Test.ts

/// <reference path="Validation.ts" />
/// <reference path="LettersOnlyValidator.ts" />
/// <reference path="ZipCodeValidator.ts" />

// Some samples to try
var strings = ['Hello', '98052', '101'];
// Validators to use
var validators: { [s: string]: Validation.StringValidator; } = {};
validators['ZIP code'] = new Validation.ZipCodeValidator();
validators['Letters only'] = new Validation.LettersOnlyValidator();
// Show whether each string passed each validator
strings.forEach(s => {
    for (var name in validators) {
        console.log('"' + s + '" ' + (validators[name].isAcceptable(s) ? ' matches ' : ' does not match ') + name);
    }
});

在項目中新建好以上四個文件,然后我們編譯項目,如果我們代碼編寫沒錯的話,是能夠編譯通過的。另外,我們可以見到后面三個文件開頭有類似於 C# 的文檔注釋,這是告訴 TypeScript 編譯器該文件依賴於哪些文件,假如依賴的文件不存在的話,編譯就會不通過。當然我們不寫也是可以的,只不過編譯器在編譯時不會幫我們檢查,一般來說,還是建議寫上。

另外,在引用編譯生成的 JavaScript 文件時,我們需要注意好順序。以上面的代碼為例,我們在 Html 代碼中已經這么引用。

<script src="Validation.js" type="text/javascript" />
<script src="LettersOnlyValidator.js" type="text/javascript" />
<script src="ZipCodeValidator.js" type="text/javascript" />
<script src="Test.js" type="text/javascript" />
  • 外部模塊

在上面的方式中,瀏覽器會把 4 個 JavaScript 都加載。但某些時候,我們並不需要全部都用上,應該實現按需加載。那么在 TypeScript 中如何實現呢,很簡單,只需要稍微修改一下就行。

Validation.ts

export interface StringValidator {
    isAcceptable(s: string): boolean;
}

LettersOnlyValidator.ts

import validation = require('./Validation');
var lettersRegexp = /^[A-Za-z]+$/;
export class LettersOnlyValidator implements validation.StringValidator {
    isAcceptable(s: string) {
        return lettersRegexp.test(s);
    }
}

ZipCodeValidator.ts

import validation = require('./Validation');
var numberRegexp = /^[0-9]+$/;
export class ZipCodeValidator implements validation.StringValidator {
    isAcceptable(s: string) {
        return s.length === 5 && numberRegexp.test(s);
    }
}

Test.ts

import validation = require('./Validation');
import zip = require('./ZipCodeValidator');
import letters = require('./LettersOnlyValidator');

// Some samples to try
var strings = ['Hello', '98052', '101'];
// Validators to use
var validators: { [s: string]: validation.StringValidator; } = {};
validators['ZIP code'] = new zip.ZipCodeValidator();
validators['Letters only'] = new letters.LettersOnlyValidator();
// Show whether each string passed each validator
strings.forEach(s => {
    for (var name in validators) {
        console.log('"' + s + '" ' + (validators[name].isAcceptable(s) ? ' matches ' : ' does not match ') + name);
    }
});

修改完之后,編譯。。。不通過!

看官們可能覺得我坑爹了,辛辛苦苦敲這么一大段后,竟然編譯不通過。這里,我們先說說模塊加載的兩種規范。

CommonJS 規范:

CommonJS 目標在於實現一個類似於 Python、Ruby 等語言的標准庫。而 NodeJS 使用的就是這一規范。

var m = require('mod');
exports.t = m.something + 1;

AMD 規范:

由於上面的 CommonJS 規范所實現的加載是同步的,但實際上,我們的瀏覽器更需要的是異步加載,因此 AMD 規范應運而生。

define(["require", "exports", 'mod'], function(require, exports, m) {
    exports.t = m.something + 1;
});

那么 TypeScript 使用哪種規范呢?答案是兩種都可以,看需要選擇其中一種。

回到我們的項目,修改項目屬性。

QQ截圖20150620231137

將這里修改為 AMD 或者 CommonJS,然后就可以通過編譯了。

  • Export =

在上面的代碼中,我們導出模塊的根是文件,因此需要寫成 zip.ZipCodeValidator 這種形式,那為什么不簡化一下呢?直接用 ZipCodeValidator 多好。Export = 就可以幫助我們做這件事。

Validation.ts

export interface StringValidator {
    isAcceptable(s: string): boolean;
}

LettersOnlyValidator.ts

import validation = require('./Validation');
var lettersRegexp = /^[A-Za-z]+$/;
class LettersOnlyValidator implements validation.StringValidator {
    isAcceptable(s: string) {
        return lettersRegexp.test(s);
    }
}
export = LettersOnlyValidator;

ZipCodeValidator.ts

import validation = require('./Validation');
var numberRegexp = /^[0-9]+$/;
class ZipCodeValidator implements validation.StringValidator {
    isAcceptable(s: string) {
        return s.length === 5 && numberRegexp.test(s);
    }
}
export = ZipCodeValidator;

Test.ts

import validation = require('./Validation');
import zipValidator = require('./ZipCodeValidator');
import lettersValidator = require('./LettersOnlyValidator');

// Some samples to try
var strings = ['Hello', '98052', '101'];
// Validators to use
var validators: { [s: string]: validation.StringValidator; } = {};
validators['ZIP code'] = new zipValidator();
validators['Letters only'] = new lettersValidator();
// Show whether each string passed each validator
strings.forEach(s => {
    for (var name in validators) {
        console.log('"' + s + '" ' + (validators[name].isAcceptable(s) ? ' matches ' : ' does not match ') + name);
    }
});
  • 別名

module Shapes {
    export module Polygons {
        export class Triangle { }
        export class Square { }
    }
}

import polygons = Shapes.Polygons;
var sq = new polygons.Square(); // Same as 'new Shapes.Polygons.Square()'

這樣寫 import,下面的代碼就可以簡寫一下。需要注意的是,不支持 require 引入的模塊。

  • declare 關鍵字

有時候我們需要定義全局變量,那么我們就需要增加 declare 關鍵字。

declare ver myString;

那么 myString 變量就是全局的了。這功能在定義全局模塊時很有作用。


免責聲明!

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



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