TypeScript:类(Classes)
传统的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)的更多相关文章
- (转) 学习C++ -> 类(Classes)的定义与实现
学习C++ -> 类(Classes)的定义与实现 一."类" 的介绍 在C++中, 用 "类" 来描述 "对象", 所谓的&q ...
- 从C#到TypeScript - 类
总目录 从C#到TypeScript - 类型 从C#到TypeScript - 高级类型 从C#到TypeScript - 变量 从C#到TypeScript - 接口 从C#到TypeScript ...
- .NET手撸绘制TypeScript类图——上篇
.NET手撸绘制TypeScript类图--上篇 近年来随着交互界面的精细化,TypeScript越来越流行,前端的设计也越来复杂,而类图正是用简单的箭头和方块,反映对象与对象之间关系/依赖的好方式. ...
- .NET手撸绘制TypeScript类图——下篇
.NET手撸绘制TypeScript类图--下篇 在上篇的文章中,我们介绍了如何使用.NET解析TypeScript,这篇将介绍如何使用代码将类图渲染出来. 注:以防有人错过了,上篇链接如下:http ...
- typescript类(学习笔记非干货)
我们声明一个 Greeter类.这个类有3个成员:一个叫做greeting的属性,一个构造函数和一个greet方法. We declare a Greeter class. This class ha ...
- yii 核心类classes.php详解(持续更新中...)
classes.php在yii运行的时候将被自动加载,位于yii2文件夹底下. <?php /** * Yii core class map. * * This file is automati ...
- typescript类的修饰符
学习过java的小姐姐,小哥哥应该很好理解,但还是啰嗦的写出来! typescript里面定义属性的时候给我们提供了 三种修饰符 public :公有 在当前类里面. 子类 .类外面都可以访问 pro ...
- typescript类与继承
/* 1.vscode配置自动编译 1.第一步 tsc --inti 生成tsconfig.json 改 "outDir": "./js", 2.第二步 任务 ...
- Python中的类(classes)
Python的类机制使用尽可能少的新语法和语义将类引入语言.python的类提供了面向对象程序设计语言所有的 标准特性:类继承机制允许有多个基类,一个派生类可以覆盖基类中的任何方法,一个方法可以使用相 ...
随机推荐
- linux编程中printf显示不加换行的缓冲问题
最近在编写linux网络编程时,总是遇到这样的事,程序逻辑没错误,但是程序运行到某个地方就停在那里了,后来才发现在prinrf()中加入换行能正常运行了,如“ printf("123&quo ...
- mysql 重复数据防止插入:)
insert into table (id, name, age) values(1, "A", 19) on duplicate key update name=values(n ...
- sql良好习惯
我们做软件开发的,大部分人都离不开跟数据库打交道,特别是erp开发的,跟数据库打交道更是频繁,存储过程动不动就是上千行,如果数据量大,人员流动大,那么我么还能保证下一段时间系统还能流畅的运行吗?我么还 ...
- div层叠顺序额
在模态窗体中打开新div,结果该div不显示 该div不是没有显示,而是显示了,但是被这个模态状体挡住了 解决方法:修改z-index这个参数 该参数越大则显示在越上层,即可见
- 用window.showModelDialog() 打开的页面的返回值
有两个页面也个 Default1.aspx 另外一个是 Default2.aspx Default1.aspx 有个按钮是用来打开Default2.aspx页面的 按钮的js代码是 var win ...
- oracle创建用户、授予权限及删除用户
创建用户 oracle对表空间 USERS 无权限 alter user 用户名 quota unlimited on users; //创建临时表空间 create temporary ta ...
- .net获取IP和MAC地址
获取IP 解决request.UserHostAddress取不到真实IP private string GetClientIP() { string result = HttpConte ...
- OA项目之导入
内容显示页: protected void btnIMP_Click(object sender, EventArgs e) { Response.Redire ...
- Android之ImageView 设置宽高
方案一: 设置布局参数 imageView.setLayoutParams(new LinearLayout.LayoutParams(newWidth, newWidth));
- Verilog之电平检测
检测低电平为例 module detect_module ( CLK, RSTn, RX_Pin_In, H2L_Sig ); input CLK; input RSTn; input RX_Pin_ ...