前端數據驗證在改善用戶體驗上有很大做用,在學了以前的知識的時候,咱們極可能會寫出如下代碼:javascript
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); } });
那麼這段代碼最大的問題是什麼呢?一個是無法複用,驗證的封裝和驗證過程在同一個文件,驗證的封裝已是能夠複用的。另外一個是接口和兩個實現的類都直接掛接在全局變量上,假如數量一多的話,將會污染整個全局變量。前端
模塊化就是爲了解決這一問題而誕生的。java
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.tsui
module Validation { export interface StringValidator { isAcceptable(s: string): boolean; } }
LettersOnlyValidator.tsspa
/// <reference path="Validation.ts" /> module Validation { var lettersRegexp = /^[A-Za-z]+$/; export class LettersOnlyValidator implements StringValidator { isAcceptable(s: string) { return lettersRegexp.test(s); } } }
ZipCodeValidator.tscode
/// <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.tsblog
/// <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 目標在於實現一個相似於 Python、Ruby 等語言的標準庫。而 NodeJS 使用的就是這一規範。
var m = require('mod'); exports.t = m.something + 1;
因爲上面的 CommonJS 規範所實現的加載是同步的,但實際上,咱們的瀏覽器更須要的是異步加載,所以 AMD 規範應運而生。
define(["require", "exports", 'mod'], function(require, exports, m) { exports.t = m.something + 1; });
那麼 TypeScript 使用哪一種規範呢?答案是兩種均可以,看須要選擇其中一種。
回到咱們的項目,修改項目屬性。
將這裏修改成 AMD 或者 CommonJS,而後就能夠經過編譯了。
在上面的代碼中,咱們導出模塊的根是文件,所以須要寫成 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 ver myString;
那麼 myString 變量就是全局的了。這功能在定義全局模塊時頗有做用。