博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
TypeScript语言特性(下)
阅读量:5876 次
发布时间:2019-06-19

本文共 6701 字,大约阅读时间需要 22 分钟。

引言:TypeScript是一个开源的、跨平台且带有类型系统的JavaScript超集,它可以编译为纯JavaScript,然后运行在任意的浏览器和其他环境中。

本文选自《Learning TypeScript中文版》一书,在上篇文章中我们了解了TypeScript的类型、变量、基本类型和运算符等语言特性,本文将继续向您介绍流程控制语句、函数、类、接口以及命名空间等语言特性。

流程控制语句

这一节将描述 TypeScript 中的选择语句、循环语句和分支语句。

单一选择结构(if)

下面这段代码声明了一个boolean类型的变量isValid。然后,一个if语句会判断isValid的值是否为true。如果判断结果为true,则在屏幕上会显示消息Is valid!。

var isValid : boolean = true;if(isValid) {  alert("is valid!");}

双选择结构(if…else)

下面这段代码声明了一个boolean类型的变量isValid。然后,一个 if语句会判断isValid的值是否为true。如果判断结果为true,则在屏幕上会显示消息Is valid!。另一方面,如果判断结果为 false,在屏幕上会显示消息Is NOT valid!。

var isValid : boolean = true;if(isValid) {  alert("Is valid!");}else {  alert("Is NOT valid!");}

三元操作符(?)

三元操作符是双选择结构的一种替代形式。

var isValid : boolean = true;var message = isValid ? "Is valid!" : "Is NOT valid!";alert(message);

上面这段代码声明了一个boolean类型的变量isValid。然后它判断操作符 ? 左边的变量或表达式是否等于true。

如果判断结果为true,则会执行冒号左边的表达式,Is valid!会被赋值给变量message。
另一方面,如果判断结果为false,则会执行冒号右边的表达式,Is NOT valid!会被赋值给变量message。
最后,变量message的值会显示在屏幕上。

多选结构(switch)

switch语句接受一个表达式,将表达式的值与 case 语句进行匹配,然后执行关联到这种情况下的语句。switch语句经常与枚举类型的变量一起使用来提高代码的可读性。

在下面这个例子中,我们声明了一个接受枚举类型参数AlertLevel的函数。我们在这个函数内部生成一个字符串数组存储E-mail 地址然后执行switch语句。枚举变量中的每一个选项都对应着switch结构内的一个case:

enum AlertLevel{  info,  warning,  error}function getAlertSubscribers(level: AlertLevel) {  var emails = new Array
(); switch (level) { case AlertLevel.info: emails.push("cst@domain.com"); break; case AlertLevel.warning: emails.push("development@domain.com"); emails.push("sysadmin@domain.com"); break; case AlertLevel.error: emails.push("development@domain.com"); emails.push("sysadmin@domain.com"); emails.push("management@domain.com"); break; default: throw new Error("Invalid argument!"); } return emails;}getAlertSubscribers(AlertLevel.info); // ["cst@domain.com"]getAlertSubscribers(AlertLevel.warning); //["development@domain.com", "sysadmin@domain.com"]

变量level的值会与switch中所有的case值进行匹配。如果其中一个值与其匹配,那么与这个case关联的语句将会被执行。一旦这个case语句执行完毕,这个变量的值就会与下一个case进行匹配。

当一个case中的语句执行完毕后,下一个满足条件的case语句就会接着执行。如果break关键字出现在case语句中,程序就不会继续匹配接下来的case语句了。
如果没有匹配到任何case语句,程序寻找可选的default语句,如果找到,它将控制程序进入这个语句并且执行其中的代码。
如果没有找到default语句,程序将会继续执行switch表达式后面的语句。按照惯例,default语句放在最后的位置,但这并不是一个强制性的写法。

语句在顶部进行判断的循环(while)

while语句被用来在满足条件的情况下重复一个操作。比如下面这段代码,声明一个数字类型的变量i,当条件(i 小于 5)满足时,将会执行一个操作(i 加 1 然后在浏览器的控制台中打印它的值)。当这个操作完成后,将会再次判断循环的条件。

var i : number = 0;while (i < 5) {i += 1;console.log(i);}

在while语句中,语句内的操作只在while条件满足时执行。

语句在底部进行判断的循环(do…while)

do…while语句被用来重复一个操作直到条件不再被满足。比如下面这段代码,声明一个数字类型的变量i,在条件(i 小于 5)满足时一直执行一个操作(i 加 1 然后在浏览器的控制台中打印它的值)。

var i: number = 0;do {  i += 1;  console.log(i);} while (i < 5);

和while语句不一样,do…while语句会在判断while条件是否满足之前至少执行一次,不管条件是否满足。

迭代对象的属性(for…in)

for…in语句本身并不是一个坏的实践,然而它可能会被滥用。例如,迭代一个数组或者类数组对象。for…in语句的原意是枚举对象的属性。

var obj: any = { a: 1, b: 2, c: 3 };for (var key in obj) {  console.log(key + " = " + obj[key]);}// 输出:// "a = 1"// "b = 2"// "c = 3"

这段代码会沿着原型链,将继承的属性也进行枚举。for…in语句会沿着对象的原型链迭代,枚举出包括继承的属性的所有属性。如果只想枚举对象自己的属性(非继承属性),可以使用hasOwnProperty方法:

for (var key in obj) {   if (obj.hasOwnProperty(prop)) {    // prop没有被继承  }}

计数器控制循环(for)

for语句会创建一个包含三个可选表达式的循环,表达式在圆括号中用分号分隔,紧跟一个或者一些在循环中执行的语句:

for (var i: number = 0; i < 9; i++) {  console.log(i);}

上面这段代码包含一个for语句,它以声明一个变量i并初始化为0开始。第二个语句判断i是否小于9,然后每次循环的时候将i加1。

函数
就像 JavaScript 一样,TypeScript 的函数也可以通过具名或匿名的方式创建。这使我们可以根据应用中的具体情况,选择合适的方式,不论是在构建API时,或创建供其他函数调用的中间函数时。

// 具名函数function greet(name?: string): string {  if (name) {    return "Hi! " + name;  }else{    return "Hi!";  }}// 匿名函数var greet = function(name?: string): string {  if (name) {    return "Hi! " + name;  }else{    return "Hi!";  }}

正如上述代码所示,在 TypeScript 中,不仅可以为函数的参数加上类型,也可以给函数的返回值指定类型。TypeScript 会通过查看函数里的return语句,来检查返回值的类型正确与否,并且它们都不是必需的。

如果不想使用函数语法,还有另一种语法可以选择,也可以在函数的返回值类型后加上箭头(=>)操作符并不使用function关键字:

var greet = (name: string): string => {  if (name) {    return "Hi! " + name;  }  else {    return "Hi! my name is " + this.fullname;  }};

使用这种语法声明的函数通常都称作箭头函数。继续回到上述例子,还可以给greet变量添加上匹配匿名函数的类型。

var greet: (name: string) => string = function(name: string):  string {  if (name) {    return "Hi! " + name;  }else{    return "Hi!";  }};

现在我们已经学习了如何将一个变量强制描述为指定形式的函数。这在我们使用回调函数(作为另一个函数的参数)时,十分有用。

function sume(a: number, b: number, callback: (result: number)  => void) {  callback(a + b);}

在上述例子里,我们声明了一个名为sume的函数,并且指定了两个number类型的参数和第三个函数类型的callback参数。回调函数上的类型声明将会限制callback参数为一个仅接受一个number类型的参数,且无返回值的函数。

在ECMAScript 6(即最新版本的JavaScript)中,添加了基于类的面向对象编程语法。由于 TypeScript 是基于 ES6 的,所以开发者如今就已经可以开始使用基于类的面向对象的语法了。TypeScript的编译器会负责将 TypeScript 代码编译为兼容主流浏览器和平台的 JavaScript 代码。

让我们来看一个在TypeScript中定义类的例子:

class Character {  fullname: string;  constructor(firstname: string, lastname: string) {    this.fullname = firstname + " " + lastname;  }  greet(name?: string) {    if (name) {      return "Hi! " + name + "! my name is " + this.fullname;    } else {      return "Hi! my name is " + this.fullname;    }  }}var spark = new Character("Jacob", "Keyes");var msg = spark.greet();alert(msg); // "Hi! my name is Jacob Keyes"var msg1 = spark.greet("Dr. Halsey");alert(msg1); // "Hi! Dr. Halsey! my name is Jacob Keyes"

在上面的例子里,我们定义了一个名为Character的新类。这个类有三个成员:一个名为fullname的属性,一个构造函数constructor,和一个greet方法。当我们在 TypeScript 中声明类时,所有的属性和方法默认都是公共的。

你可能已经留意到,当(在对象内部)访问对象内的成员时,我们都在前面加上了this操作符,this操作符表明了这是一个成员访问操作。我们使用new操作符构造了Character类的一个实例,这会调用类的构造函数,按照定义对实例进行初始化。
为了兼容 ECMAScript 3 和 ECMAScript 5,TypeScript中的类会被编译为 JavaScript 中的函数。

接口

在 TypeScript 中,可以使用接口来确保类拥有指定的结构。

interface LoggerInterface {  log(arg: any): void;}class Logger implements LoggerInterface {  log(arg) {    if (typeof console.log === "function") {      console.log(arg);    } else {      alert(arg);    }  }}

在上面的例子里,我们定义了一个名为loggerInterface的接口,和一个实现了它的Logger类。TypeScript也允许使用接口来约束对象。这使我们可以避免很多潜在的小错误,尤其是在写对象字面量时:

interface UserInterface{  name : string;  password : string;}var user : UserInterface = {  name : "",  pasword : "" // password遗漏错误属性};

命名空间

命名空间,又称内部模块,被用于组织一些具有某些内在联系的特性和对象。命名空间能够使代码结构更清晰,可以使用namespace和export关键字,在TypeScript中声明命名空间。

namespace Geometry{  interface VectorInterface {  /* ... /  }  export interface Vector2dInterface {    /* ... */  }  export interface Vector3dInterface {    /* ... /  }  export class Vector2d implements VectorInterface, Vector2dInterface {    /* ... /  }  export class Vector3d implements VectorInterface, Vector3dInterface {    /* ... /  }}var vector2dInstance: Geometry.Vector2dInterface = new Geometry.Vector2d();var vector3dInstance: Geometry.Vector3dInterface = new Geometry.Vector3d();

在上面的例子里,我们声明了一个包含了Vector2d、Vector3d类和VectorInterface、Vector2dInterface、Vector3dInterface接口的命名空间。注意,命名空间内的第一个接口声明前并没有export关键字。所以,在命名空间的外部,我们访问不到它。

本篇文章的上半部分请访问。
想及时获得更多精彩文章,可在微信中搜索“博文视点”或者扫描下方二维码并关注。
图片描述

你可能感兴趣的文章
驯服 Tiger: 并发集合 超越 Map、Collection、List 和 Set
查看>>
Winform开发框架之权限管理系统改进的经验总结(3)-系统登录黑白名单的实现...
查看>>
Template Method Design Pattern in Java
查看>>
MVC输出字符串常用四个方式
查看>>
LeetCode – LRU Cache (Java)
查看>>
JavaScript高级程序设计--对象,数组(栈方法,队列方法,重排序方法,迭代方法)...
查看>>
【转】 学习ios(必看经典)牛人40天精通iOS开发的学习方法【2015.12.2
查看>>
nginx+php的使用
查看>>
在 ASP.NET MVC 中使用异步控制器
查看>>
SQL语句的执行过程
查看>>
Silverlight开发历程—动画(线性动画)
查看>>
详解Linux中Load average负载
查看>>
HTTP 协议 Cache-Control 头——性能啊~~~
查看>>
丢包补偿技术概述
查看>>
PHP遍历文件夹及子文件夹所有文件
查看>>
WinForm程序中两份mdf文件问题的解决
查看>>
【转】唯快不破:创业公司如何高效的进行产品研发管理
查看>>
程序计数器、反汇编工具
查看>>
Android N: jack server failed
查看>>
007-Shell test 命令,[],[[]]
查看>>