TypeScript Class(类)
传统的JavaScript注重用函数和基于原型的继承来创建可复用的组件,但这可能让用习惯面对对象方式的程序员感到棘手,因为他们的继承和创建对象都是由类而来的。从JavaScript的下一个版本,ECMAScript 6开始,JavaScript程序员就能够用基于这种基于类的面对对象方式来创建编写自己的程序了。在TypeScript中,不需要再等JavaScript的下一个版本就已经支持开发者使用这一技术了。
类
让我们来看一个简单的基于类的例子:
class Greeter {
greeting: string;
constructor(message: string) {
this.greeting = message;
}
greet() {
return "Hello, " + this.greeting;
}
}
var greeter = new Greeter("world");
如果你之前有使用过C#或者Java,会觉得语法非常相似。我们声明一个新的类"Greeter"。这个类里面有三个成员,一个名为"greeting"的属性,一个constructor和一个"greet"方法。
你会注意到,在类里面当某一个成员使用了"this",意味着他访问的是这个类的成员。
在最后一行中,我们使用"new"来为Greeter类构造一个实例。这将会调用之前定义的构造函数,并且创建一个新的Greeter类型的对象,并且执行构造函数来初始化这个对象。
继承
在TypeScript中,我们可以使用常见的面向对象模式。当然,在基于类的编程中最基本的模式之一就是能够创建一个新的类,这个新的类继承已有的类,并对已有的类做扩展。
来看一个例子:
class Animal {
name:string;
constructor(theName: string) { this.name = theName; }
move(meters: number = 0) {
alert(this.name + " moved " + meters + "m.");
}
}
class Snake extends Animal {
constructor(name: string) { super(name); }
move(meters = 5) {
alert("Slithering...");
super.move(meters);
}
}
class Horse extends Animal {
constructor(name: string) { super(name); }
move(meters = 45) {
alert("Galloping...");
super.move(meters);
}
}
var sam = new Snake("Sammy the Python");
var tom: Animal = new Horse("Tommy the Palomino");
sam.move();
tom.move(34);
这个例子包含了TypeScript中继承的特性,当然,在其他语言中也一样。在这里,我们使用"extends"关键字来创建一个子类。你可以看到,这里"Horse"和"Snake"两个子类都基于"Animal"这个父类,并且对其特性进行了扩展。在这里,我们使用"extends"关键字来创建一个子类。你可以看到,这里"Horse"和"Snake"两个子类都基于"Animal"这个基类并且获取其特性。
例子也提现出在子类中可以重写基类中的方法以达到重写后的方法是在这个子类中专用。这里的"Horse"和"Snake"都创建了"move"这个方法,这样就重写了从基类继承过来的move方法,并且在不同类中给"move"不同的方法。
公有和私有的修饰符
默认是public(公有)
你可能已经注意到了,在上面的例子中,我们并未对类的任何可见成员使用"public"关键字进行修饰。类似C#语言,需要给成员使用"public"修饰符用来明确它是可见。在TypeScript中,每个成员默认是"public"的。
你还可以给成员标记上"private",这样你就可以控制在你的类之外哪些成员是可见。我们可以像这样重写上一节的"Animal"类:
class Animal {
private name:string;
constructor(theName: string) { this.name = theName; }
move(meters: number) {
alert(this.name + " moved " + meters + "m.");
}
}
理解Private(私有)
TypeScript是个构造类型的系统。当我们对两个类型进行比较的时候,无论它们是从哪里来,如果所有成员的类型都是兼容的,那么我们可以认为他们的类型也是兼容的。
当我们比较的类型中含有"private"(私有)成员,则我们就需要不同的对待了。两个类型(假如是A和B)被认为是兼容的,如果A类型含有一个私有成员,那么B类型就必须也有一个私有成员并且与A类型的私有成员源自同一处声明。
让我们用一个例子来更好的看看私有成员在实践中如何运用:
class Animal {
private name:string;
constructor(theName: string) { this.name = theName; }
}
class Rhino extends Animal {
constructor() { super("Rhino"); }
}
class Employee {
private name:string;
constructor(theName: string) { this.name = theName; }
}
var animal = new Animal("Goat");
var rhino = new Rhino();
var employee = new Employee("Bob");
animal = rhino;
animal = employee; // 错误: Animal 和 Employee 不兼容
在这个例子中,我们有一个"Animal"和一个"Rhino","Rhino"是"Animal"的一个子类。我们还有一个新的类"Employee",它看上去跟"Animal"类是完全相同的。我们给这些类分别创建实例,并且对他们进行相互赋值,看下将会发生什么。因为"animal"和"rhino"的私有成员都是从"Animal"类定义的"private name: string"共享而来的,所以他们是兼容的。然而,"employee"的情况却不是这样的。当我们试图将"employee"赋值给"animal",我们得到了一个错误,他们的类型是不兼容的。尽管"Employee"也有一个名称是"name"的私有成员,但它和在"Animal"中的私有成员"name"还是不相同的。
参数属性
关键字"public"和"private"通过创建参数属性的方式给我们提供了创建和初始化类的成员的便捷方式。这个特性让你可以一个步骤就创建和初始化成员。这里有一个之前例子的进一步修改。注意我们是如何在constructor中将"name"使用"private name: string"的便捷方式完整的创建并初始化成这个类的私有成员"name"的。
class Animal {
constructor(private name: string) { }
move(meters: number) {
alert(this.name + " moved " + meters + "m.");
}
}
var goat = new Animal("Goat");
goat.move(25); // Goat moved 25 m.
通过这种方式使用"private"来创建和初始化私有成员,"public"也一样。
访问器
TypeScript提供 getters/setters 的方式来拦截对于对象成员的访问。它让我们可以更精确的控制如何对对象成员的进行访问。
让我们来将一个类改写成用"get"和"set"。首先,我们从一个没有"get"和"set"的例子开始:
class Employee {
fullName: string;
}
var employee = new Employee();
employee.fullName = "Bob Smith";
if (employee.fullName) {
alert(employee.fullName);
}
以上代码允许我们随意设置fullName,可能我们会觉得这样比较直接和方便,但这么随心所欲的改变名字也可能会导致问题。
在这个版本中,我们将给被允许修改员工信息的用户一个可用的密码。在对fullName进行"set"访问的之前,我们会以检查密码来代替允许直接修改。我们添加一个相应的"get"让之前的例子依然能实现。
var passcode = "secret passcode";
class Employee {
private _fullName: string;
get fullName(): string {
return this._fullName;
}
set fullName(newName: string) {
if (passcode && passcode == "secret passcode") {
this._fullName = newName;
}
else {
alert("Error: Unauthorized update of employee!");
}
}
}
var employee = new Employee();
employee.fullName = "Bob Smith";
if (employee.fullName) {
alert(employee.fullName);
}
为了证明现在访问需要密码,我们可以修改密码,然后我们会发现,当密码不符合的时候会弹出提示"Error: Unauthorized update of employee!"(错误:没有修改employee的权限)。
注意:访问器需要我们将文件以ECMAScript5编程输出。
tsc --target ES5 your.ts
静态属性
到此为止,我们只谈到类的实例成员,那些只有实例化后才初始化并且显示的成员。我们还可以为类的创建静态成员,那些在类本身可见而非实在实例上可见。在这个例子中,我们使用"static"来修饰"origin",因为他是所有Grid都会用到的东西。每个实例想要访问这个属性,都需要在前面加上类名。这就像要在实例前面加上"this"来访问这个实例,这里我们将使用"Grid."来访问静态属性。
class Grid {
static origin = {x: 0, y: 0};
calculateDistanceFromOrigin(point: {x: number; y: number;}) {
var xDist = (point.x - Grid.origin.x);
var yDist = (point.y - Grid.origin.y);
return Math.sqrt(xDist * xDist + yDist * yDist) / this.scale;
}
constructor (public scale: number) { }
}
var grid1 = new Grid(1.0); // 1x 规模
var grid2 = new Grid(5.0); // 5x 规模
alert(grid1.calculateDistanceFromOrigin({x: 10, y: 10}));
alert(grid2.calculateDistanceFromOrigin({x: 10, y: 10}));
高级技巧
构造函数
当你在TypeScript中声明一个类的同时,你也定义了很多东西。首先就是这个类的实例类型。
class Greeter {
greeting: string;
constructor(message: string) {
this.greeting = message;
}
greet() {
return "Hello, " + this.greeting;
}
}
var greeter: Greeter;
greeter = new Greeter("world");
alert(greeter.greet());
这里,当我们写"var greeter: Greeter",我们就已经将"Greeter"类的实例类型定义为"Greeter"了。这对于用过其它面向对象语言的程序员而言已经习以为常了。
我们也同时的创建了一个称为构造函数的值,当我们使用"new"来为类创建实例的时候,我们将会调用这个函数。让我们结合实践,在编译后的JavaScript中看看上面的这个例子吧:
var Greeter = (function () {
function Greeter(message) {
this.greeting = message;
}
Greeter.prototype.greet = function () {
return "Hello, " + this.greeting;
};
return Greeter;
})();
var greeter;
greeter = new Greeter("world");
alert(greeter.greet());
在这里,"var Greeter"是指定构造函数。当我们使用"new"并且执行这个函数之后,便会得到一个类的实例。这个构造函数包含了类的所有的静态成员。换种说法,即类有静态部分和实例部分。
让我们稍微修改下例子看看它们的不同之处:
class Greeter {
static standardGreeting = "Hello, there";
greeting: string;
greet() {
if (this.greeting) {
return "Hello, " + this.greeting;
}
else {
return Greeter.standardGreeting;
}
}
}
var greeter1: Greeter;
greeter1 = new Greeter();
alert(greeter1.greet());
// 上下代码效果做对比
var greeterMaker: typeof Greeter = Greeter;
greeterMaker.standardGreeting = "Hey there!";
var greeter2:Greeter = new greeterMaker();
alert(greeter2.greet());
在这个例子中,"greeter1"和之前例子是一样的。我们实例化了"Greeter"类,并且使用这个对象。结果也和之前的例子一样。
接下来,我们直接使用这个类,我们创建了一个名为"greeterMaker"的新变量。这个变量保存了这个类,换种说法即保存了这个构造函数。这里我们使用"typeof Greeter",这么做的话"greeterMaker"的类型就成了"Greeter"类的类型,而非"Greeter"的实例的类型("Greeter"类的实例类型为"Greeter")。更准确的说,"给我Greeter类的类型",也就是构造函数的类型。这个类包含"Greeter"类的所有静态成员和创建"Greeter"类的实例的构造函数。同之前的例子一样,我们对"greeterMaker"使用"new",用来创建"Greeter"的实例并且触发。
将类当作接口一样使用
正如我们在上一节所说的,声明一个类的同时会创建其他两个东西:这个类的实例类型和一个构造函数。因为类能够创建类型,所以在使用interface(接口)的地方都可以使用class(类)。
class Point {
x: number;
y: number;
}
interface Point3d extends Point {
z: number;
}
var point3d: Point3d = {x: 1, y: 2, z: 3};
TypeScript Class(类)的更多相关文章
- TypeScript 素描 - 类
本文虽然是学自官方教程而来,但是也融入了自己的理解,而且对官方的例子做了一些修改 /* 类 面向对象编程的一大核心 使用C#.Java进行编程的朋友肯定已经是不能够再熟悉了 TypeScript的类与 ...
- TypeScript入门四:TypeScript的类(class)
TypeScript类的基本使用(修饰符) TypeScript类的抽象类(abstract) TypeScript类的高级技巧 一.TypeScript类的基本使用(修饰符) TypeScript的 ...
- TypeScript入门-类
▓▓▓▓▓▓ 大致介绍 在ECMASript6中引入了类这一概念,通过class声明一个类.对于学习过C和C++的人应该不会陌生 ▓▓▓▓▓▓ 类 看一个简单的类: class Greeter { g ...
- Typescript 学习 - 类
class class 并不是一种新的数据结构,只是在函数原型基础上的语法糖 class People { hand: number; constructor(hand: number) { this ...
- typescript - 4.es5与typescript的类与继承
ES5中的类与类的继承 (1)简单的类 function Person() { this.name = '张三'; this.age = 20; } var p = new Person(); ale ...
- Angular2入门:TypeScript的类 - 参数属性:定义和初始化类成员
- Angular2入门:TypeScript的类 - 定义、继承和作用域
一.定义和继承 二.public.private和protected
- typescript实现类规则
备注: 单独的 index.d.ts对于代码实现没有约束性,将约束和实现写在一个页面里有约束性,或者使用如下: // clock.interface.ts export interface Clock ...
- 从C#到TypeScript - 类
总目录 从C#到TypeScript - 类型 从C#到TypeScript - 高级类型 从C#到TypeScript - 变量 从C#到TypeScript - 接口 从C#到TypeScript ...
随机推荐
- 基于DDD的.NET开发框架 - ABP的Entity设计思想
返回ABP系列 ABP是“ASP.NET Boilerplate Project (ASP.NET样板项目)”的简称. ASP.NET Boilerplate是一个用最佳实践和流行技术开发现代WEB应 ...
- 用TypeScript开发Vue——如何通过vue实例化对象访问实际ViewModel对象
用TypeScript开发Vue--如何通过vue实例化对象访问实际ViewModel对象 背景 我个人很喜欢TypeScript也很喜欢Vue,但在两者共同使用的时候遇到一个问题. Vue的实例化对 ...
- Web端PHP代码函数覆盖率测试解决方案
1. 关于代码覆盖率 衡量代码覆盖率有很多种层次,比如行覆盖率,函数/方法覆盖率,类覆盖率,分支覆盖率等等.代码覆盖率也是衡量测试质量的一个重要标准,对于黑盒测试来说,如果你不确定自己的测试用例是否真 ...
- swagger editor使用
swagger editor使用 swagger是一套开源的API设计工具,包括Swagger UI,Swagger Editor等. Swagger Editor 其中Swagger Editor是 ...
- 正则表达式语法(msdn)
“正则表达式”描述在搜索文本正文时要匹配的一个或多个字符串.该表达式可用作一个将字符模式与要搜索的字符串相匹配的模板. 正则表达式包括普通字符(例如,a 到 z 之间的字母)和特殊字符(称为“元字符” ...
- SDRAM操作原理分析
芯片原理图 引脚原理图 指令 通过对上面指令的总结,简化出要用到的指令如下: 指令 常量名 CKE CSn RAS CASn WEn 备注 空操作 NOP 1 0 1 1 1 行激活 ACTIVE ...
- REST API (from IBM)
REST 本身是设计风格而不是标准.REST 谈论一件非常重要的事,如何正确地使用 Web标准,例如,HTTP 和 URI.想要了解 REST 最好的方式就是思索与了解 Web 及其工作方式.如果你设 ...
- MVC认知路【点点滴滴支离破碎】【二】----Razor服务器标记语言
Razor 代码块包含在 @{....}中 内嵌表达式(变量和函数)已 @ 开头 代码语句用分号结束 变量使用 var 关键字声明 字符创用引号括起来 C#代码区分大小写 C#文件的扩展是 .csht ...
- 现代软件工程 第七章 【MSF】练习与讨论
7.7 移山开发方法——比TFS敏捷更精简 几个软件学院的学生来请教阿超,同学们自豪地说,我们要用全套TFS敏捷开发模式开发项目! 真的?阿超不敢相信. 同学: 对!我们要用全5个工作项类型 – 任 ...
- js中return的用法
<!DOCTYPE html><html> <head> <meta charset="UTF-8"> <title>& ...