返回TypeScript手册总目录


传统的Javascript关注的是函数(function)和基于原型(prototype-based)的继承作为构建可重复使用组件的基本方式,但是与更舒服地使用面向对象的方式比较,这可能让程序员感到有点难受了。在面向对象中,类继承了功能, 然后对象从类中产生。从下一代版本的JS(ECMAScript6)开始,JS程序员可以使用基于类的面向对象的方式来构建应用。在TS中,我们允许程序员不用等待下一代JS出来,现在就使用这些技术,并且把它们向下编译成可在所有主流浏览器和平台上运行的JS。

先来看一个简单的基于类的例子:

class Greeter {
greeting: string;
constructor(message: string) {
this.greeting = message;
}
greet() {
return "Hello," + this.greeting;
}
} var user = new Greeter("World!");

document.writeln(user.greet());

如果你熟悉Java或者C#的话,那么这段代码对你而言很简单。首先,声明了一个类,该类有三个成员:包括一个属性greeting,一个构造函数和一个方法greet。

你会发现,在我们使用类的某个成员时,是通过this关键字来访问的,这表明它是一个成员访问。

倒数第二行我们使用new关键字创建了一个Greeter类的对象,这会调用我们之前定义的构造函数,使用Greeter模型创建了一个新的对象,然后运行构造函数进行初始化。

继承

在TS中,我们使用通用的面向对象模式。当然,在基于类的编程中,最基本模式之一就是使用继承这一特征来扩展已存在的类来创建一个新类。

来看一个例子:

class Animal {
name: string;
constructor(theName: string) {
this.name = theName;
}
move(meters: number = 0) {
alert(this.name + "移动了" + meters+"米");
}
}
class Snake extends Animal {
constructor(name: string) {
super(name);
}
move(meters = 5) {
alert("我是蛇,我在滑行...");
super.move(meters);
}
}
class Horse extends Animal {
constructor(name: string) { super(name) }
move(meters = 45) {
alert("我是马,我在奔跑!");
super.move(meters);
}
} var snakeObj = new Snake("眼镜蛇");

var horseObj: Animal = new Horse("千里马");

snakeObj.move();

horseObj.move(1000);

这个例子覆盖了TS中相当一部分继承特征。我们这里使用了extends关键字来创建一个子类。你可以看到Snake和Horse类都是Animal的子类,从而获得了父类的访问权。

这个例子也说明了,如果子类需要,就可以重写父类中的方法。例子中Snake和Horse类都创建了一个move方法,并且重写了来自Animal的move方法,这样就把父类的特有功能给了每个子类。

Private/Public修饰符

默认Public

你可能已经注意到在上面的例子中,我们没有使用关键字public就能使类的成员可见。像C#语言就必须要求每一个成员显式地标明public才是可见的。在TS中,每一个成员默认都是public的。

你仍然可以把成员标记为private,目的是你可以控制它在你的类之外是公共可见的。我们可以这样写之前的Animal类:

class Animal {
private name: string;
constructor(theName: string) {
this.name = theName;
}
move(meters: number = 0) {
alert(this.name + "移动了" + meters+"米");
}
}

理解private

TS是一个结构型系统。当比较两个不同的类型时,不用考虑它们来自哪里,如果它们的每一个成员的类型是兼容的,那么我们就说这两个类型是兼容的。

当比较具有private成员的类型时,我们就要具体分析了。对于两个兼容的类型而言,如果其中一个类有一个private成员,那么另外一个必须要有一个产生自相同声明的private成员(换言之,共用同一个声明)。

来通过一个实例来更好地理解:

class Animal {
private name: string;
constructor(theName: string) {
this.name = theName;
}
}
class Snake extends Animal {
constructor(name: string) {
super(name);
}
} class Employee {

private name: string;

constructor(theName: string) {

this.name = theName;

}

} var animalObj = new Animal("公鸡");

var snakeObj = new Snake("蛇");

var emloyeeObj = new Employee("雇员");

animalObj = snakeObj;

animalObj = emloyeeObj;//报错,因为Employee类有name的私有声明,不能把它复值给animalObj的name属性

在这个例子中,我们有三个类,其中”Snake”是Animal的子类。我们也创建了一个看上去和Animal样子一样的新类”Employee“。我们也创建了这些类的 实例,并把它们相互赋值看会发生什么。因为Animal和Snake共享同一个Animal中的声明”private name:string“,所以它们是兼容的。然而,这对Employee不是这样。当尝试将一个Employee对象赋值给Animal时,会得到一个类型不兼容的错误。虽然Employee也有一个名为name的私有成员,但是它和Animal中的那个私有成员不是同一个。

参数属性

通过创建参数类型,public和private关键字也是创建和初始化类的成员快捷方式。该属性能够让你一步创建和初始化一个成员。看下面我们修改之前例子的代码。注意我们是如何不使用”theName“而在constructor上使用快捷的”private name:string”来创建并初始化name参数。

class Employee {
constructor(private name: string) {
}
move(meters: number = 0) {
alert(this.name + "移动了" + meters+"米");
}
}

用这种方式使用private来创建和初始化一个private成员,对于public也是同样的道理。

访问器

TS支持getters和setters作为访问一个对象成员拦截方式。这对于每个对象上的一个成员是如何被访问的提供了一种细粒度控制的方式。

下面使用get和set来转换一个类。首先,先不使用setter和getter来开始这个例子。

class Employee {
fullName: string;
}
var obj = new Employee();
obj.fullName = "tkb至简";
if(obj.fullName){
alert(obj.fullName);
}

虽然允许人们直接随意地设置fullName相当方便,但是如果人们突发奇想就可以改变名字,这会让我们很困惑。

在下面这个版本中,我们允许用户修改雇员之前,先要检测确保用户有一个有效的安全密码。我们可以使用会检测安全密码的set来取代直接访问fullName。我们添加一个相应的get来允许之前的例子继续无缝运行。

var passcode = "123456";
class Employee {
private _fullName: string;
get fullName(): string {
return this._fullName;
}
set fullName(newName: string) {
if (passcode && passcode == "123456") {
this._fullName = newName;
} else {
alert("您没有权限修改雇员信息!");
}
}
}
var obj = new Employee();
obj.fullName = "tkb至简";
if (obj.fullName) {
alert(obj.fullName);
}

为了证明访问器现在正在检查安全码,我们可以修改安全码来看当安全码不匹配的时候,会不会弹出一个对话框说“您没有权限修改雇员信息!”。

修改passcode为12345时,弹出错误提醒:

注意:访问器要求你必须将编译器设置为输出ECMAScript 5。

静态属性

说到这里,我们目前只讨论了类的实例成员,它们只有当类实例化时才会出现在对象上。我们也可以创建静态成员,它们对于类自身都是可见的而不是实例。下面的例子,我们在origin上使用了static关键字,因为它对于所有的grids都是通用的。每一个实例都可以通过附加的类名来访问这个值。和实例访问之前加上this关键字相似。

class Grid {
static orgin = { x: 0, yield: 0 };
calculateDistanceFromOrgin(point: { x: number,y:number }) {
var xDist = point.x - Grid.orgin.x;
var yDist = point.y - Grid.orgin.y;
return Math.sqrt(xDist * xDist + yDist * yDist) * this.scale;
}
constructor(public scale: number) {}
}
var grid1 = new Grid(1.0);//比例尺为1
var grid2 = new Grid(5.0);//比例尺为5
alert(grid1.calculateDistanceFromOrgin({ x: 10, y: 10 }));
alert(grid2.calculateDistanceFromOrgin({ x: 10, y: 10 }));

高级技术点

构造函数

在TS中声明一个类的时候,实际上是一次创建了多个声明。第一个是实例类的类型。

class Greeter {
greeting: string;
constructor(message: string) {
this.greeting = message;
}
greet() {
return "Hello," + this.greeting;
}
} var user:Greeter;
user = new Greeter("World!");

document.writeln(user.greet());

这里,当我们声明”var user:Greeter;”时,我们使用了Greeter作为Greeter类的实例的类型。这个对于来自面向对象语言的程序员来说几乎是第二个特性。

我们也通过调用构造函数创建其他值。这个函数是在我们new出类的实例的时候调用的。为了理解实践中它是什么样子,让我们通过上面的TS代码生成的JS代码来看一下:

var Greeter = (function () {
function Greeter(message) {
this.greeting = message;
}
Greeter.prototype.greet = function () {
return "Hello," + this.greeting;
};
return Greeter;
})();
var user = new Greeter("World!");
document.writeln(user.greet());

这里,“var user”将会被构造函数赋值。当我们调用new并运行这个函数的时候,我们得到了该类的一个实例。构造函数也包含了该类的所有静态成员。思考每个类的另外一种方式是类有实例的一面和静态的一面。

稍微修改一下例子看一下不同:

class Greeter {
static standardGreeting = "Hello,There";
greeting: string;
greet() {
if (this.greeting) {
return "Hello," + this.greeting;
} else {
return Greeter.standardGreeting;
}
}
} var user: Greeter;

user = new Greeter();

alert(user.greet()); var greetMaker: typeof Greeter=Greeter;

greetMaker.standardGreeting = "Hey,there!";

var user2: Greeter = new greetMaker();

alert(user2.greet());

这个例子中,“user”和之前运行相似。我们实例化Greeter类,然后使用了这个对象。这个之前已经看到过。

下一个,然后我们直接只用类。这里我们创建了一个新的变量叫做“greetMaker”。这个变量保持了类本身,或者说成是它的构造函数。这里我们使用了typeof Greeter,意思是“给我们Greeter类本身的类型”而不是类型的实例。或者,更准确地说,“给我叫做Greeter的标志的类型”,它是构造函数的类型。这个类型包含了Greeter的所有的静态成员和创建Greeter类实例的构造函数。

使用类作为接口

正如之前说的,一个类的声明创建了两样东西:代表类的实例的类型和构造函数。因为类创建了类型,所以你可以在你可以使用接口的地方使用它们,例如:

class Point {
x: number;
y: number;
} interface Point3D extends Point {

z: number;

}

var point3D: Point3D = { x: 1, y: 2, z: 3 };

TypeScript:类(Classes)的更多相关文章

  1. (转) 学习C++ -> 类(Classes)的定义与实现

    学习C++ -> 类(Classes)的定义与实现 一."类" 的介绍    在C++中, 用 "类" 来描述 "对象", 所谓的&q ...

  2. 从C#到TypeScript - 类

    总目录 从C#到TypeScript - 类型 从C#到TypeScript - 高级类型 从C#到TypeScript - 变量 从C#到TypeScript - 接口 从C#到TypeScript ...

  3. .NET手撸绘制TypeScript类图——上篇

    .NET手撸绘制TypeScript类图--上篇 近年来随着交互界面的精细化,TypeScript越来越流行,前端的设计也越来复杂,而类图正是用简单的箭头和方块,反映对象与对象之间关系/依赖的好方式. ...

  4. .NET手撸绘制TypeScript类图——下篇

    .NET手撸绘制TypeScript类图--下篇 在上篇的文章中,我们介绍了如何使用.NET解析TypeScript,这篇将介绍如何使用代码将类图渲染出来. 注:以防有人错过了,上篇链接如下:http ...

  5. typescript类(学习笔记非干货)

    我们声明一个 Greeter类.这个类有3个成员:一个叫做greeting的属性,一个构造函数和一个greet方法. We declare a Greeter class. This class ha ...

  6. yii 核心类classes.php详解(持续更新中...)

    classes.php在yii运行的时候将被自动加载,位于yii2文件夹底下. <?php /** * Yii core class map. * * This file is automati ...

  7. typescript类的修饰符

    学习过java的小姐姐,小哥哥应该很好理解,但还是啰嗦的写出来! typescript里面定义属性的时候给我们提供了 三种修饰符 public :公有 在当前类里面. 子类 .类外面都可以访问 pro ...

  8. typescript类与继承

    /* 1.vscode配置自动编译 1.第一步 tsc --inti 生成tsconfig.json 改 "outDir": "./js", 2.第二步 任务 ...

  9. Python中的类(classes)

    Python的类机制使用尽可能少的新语法和语义将类引入语言.python的类提供了面向对象程序设计语言所有的 标准特性:类继承机制允许有多个基类,一个派生类可以覆盖基类中的任何方法,一个方法可以使用相 ...

随机推荐

  1. numpy.concatenate

    import numpy as np a = np.array([[1, 2], [3, 4]]) a.shape Out[3]: (2, 2) b = np.array([[5, 6]]) b.sh ...

  2. XidianOJ 1120 Gold of Orz Pandas

    题目描述 Orz Panda is addicted to one RPG game. To make his character stronger, he have to fulfil tasks ...

  3. jquery ajax load

    jQuery load() 方法 jQuery load() 方法是简单但强大的 AJAX 方法. load() 方法从服务器加载数据,并把返回的数据放入被选元素中. 语法: $(selector). ...

  4. linux配置的问题

    1 从系统设置-文本设置中把双拼删掉 2 通过sudo passwd root 修改root密码 3 通过su获取root权限 4 通过sudo pppoeconf输入宽带帐号密码 5 把更新源修改成 ...

  5. diocp_tcp_client单元源码与注释

    (* * Unit owner: d10.天地弦 * blog: http://www.cnblogs.com/dksoft * homePage: www.diocp.org * * 2015-02 ...

  6. ORACLE 数据的逻辑组成

    数据块(block) Oracle数据块(Data Block)是一组连续的操作系统块.分配数据库块大小是在Oracle数据库创建时设置的,数据块是Oracle读写的基本单位.数据块的大小一般是操作系 ...

  7. WebService 基础使用&cxf第三方Service使用

    1.通过Jax-ws自己发布一个webservice 解析:用webservice发布HelloWorld JAX-WS本质就是通过Socket来实现的.2.WSDL文档描述如何直接变成java代码 ...

  8. Ubuntu下搭建Android编译环境

    Ubuntu一台新机器的一些环境搭建新增一个3TB的硬盘,挂载方法,大于2TB的得parted来进行分区1: sudo parted /dev/sda2: mklabel gpt3: unit TB4 ...

  9. Nginx配置配置文件nginx.conf的设置

    引用自:http://www.ha97.com/5194.html #定义Nginx运行的用户和用户组user www www; #nginx进程数,建议设置为等于CPU总核心数.worker_pro ...

  10. java异常与处理

    1:Java中的所有不正常类都继承于Throwable类.Throwable主要包括两个大类,一个是Error类,另一个是Exception类: 2:其中Error类中包括虚拟机错误和线程死锁,一旦E ...