前端数据验证在改善用户体验上有很大做用,在学了以前的知识的时候,咱们极可能会写出如下代码: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 变量就是全局的了。这功能在定义全局模块时颇有做用。