前言
hello world欢迎来到前端的新世界
😜当前文章系列专栏:Typescript
🐱👓博主在前端领域还有很多知识和技术需要掌握,正在不断努力填补技术短板。(如果出现错误,感谢大家指出)🌹
💖感谢大家支持!您的观看就是作者创作的动力
传统的JavaScript的程序使用函数和基于原型的继承来创建可重用的组件,但对于熟悉使用面向对象方式的程序员来讲就有些棘手,因为他们用的是基于类的继承并且对象是由类构建出来从ECMAScript 2015,也就是ECMAScript 6开始,JavaScript程序员将能够使用基于类的面向对象的方式。使用TypeScript,我们允许开发者现在就使用这些特性,并且编译后的JavaScript可以在所有主流浏览器和平台上运行,而不需要等到下个JavaScript的版本。
类
下面看一个使用类的例子:
class Greeter {<font></font> greeting: string; constructor(message: string) { this.greeting = message;<font></font> }<font></font> greet() {<font></font> return "Hello, " + this.greeting;<font></font> }<font></font> }<font></font> <font></font> let greeter = new Greeter("world");
如果你使用过C#或Java,你会对这种语法非常熟悉。我们声明一个 Greeter类。这个类有3个成员:一个叫做greeting的属性,一个构造函数和一个greet方法。
你会注意到,我们在引用任何一个类成员的时候都用了this。它表示我们访问的是类的成员。
最后一行,我们使用new构造了Greeter类的一个实例。它会调用之前定义的构造函数,创建一个 Greeter类型的新对象,并执行构造函数初始化它。
继承
在TypeScript里,我们可以使用常用的面向对象模式。当然,基于类的程序设计中最基本的模式是允许使用继承来扩展现有的类。
看下面的例子:
class Animal {<font></font> name:string; constructor(theName: string) { this.name = theName; }<font></font> move(distanceInMeters: number = 0) { console.log(`${this.name} moved ${distanceInMeters}m.`);<font></font> }<font></font> }<font></font> <font></font> class Snake extends Animal { constructor(name: string) { super(name); }<font></font> move(distanceInMeters = 5) { console.log("Slithering..."); super.move(distanceInMeters);<font></font> }<font></font> }<font></font> <font></font> class Horse extends Animal { constructor(name: string) { super(name); }<font></font> move(distanceInMeters = 45) { console.log("Galloping..."); super.move(distanceInMeters);<font></font> }<font></font> }<font></font> <font></font> let sam = new Snake("Sammy the Python"); let tom: Animal = new Horse("Tommy the Palomino");<font></font> <font></font> sam.move();<font></font> tom.move(34);
这个例子展示了TypeScript中继承的一些特征,它们与其它语言类似。 我们使用 extends关键字来创建子类。你可以看到Horse和Snake类是基类Animal的子类,并且可以访问其属性和方法。
包含构造函数的派生类必须调用super(),它会执行基类的构造方法。
这个例子演示了如何在子类里可以重写父类的方法。 Snake类和Horse类都创建了move方法,它们重写了从Animal继承来的move方法,使得move方法根据不同的类而具有不同的功能。 注意,即使 tom被声明为Animal类型,但因为它的值是Horse,tom.move(34)会调用Horse里的重写方法:
Slithering... Sammy the Python moved 5m. Galloping... Tommy the Palomino moved 34m.
公共,私有与受保护的修饰符
默认为public
在上面的例子里,我们可以自由的访问程序里定义的成员。 如果你对其它语言中的类比较了解,就会注意到我们在之前的代码里并没有使用 public来做修饰;例如,C#要求必须明确地使用public指定成员是可见的。 在TypeScript里,成员都默认为 public。
你也可以明确的将一个成员标记成public。 我们可以用下面的方式来重写上面的 Animal类:
class Animal { public name: string; public constructor(theName: string) { this.name = theName; } public move(distanceInMeters: number) { console.log(`${this.name} moved ${distanceInMeters}m.`);<font></font> }<font></font> }<font></font>
理解private
当成员被标记成private时,它就不能在声明它的类的外部访问。比如:
class Animal { private name: string; constructor(theName: string) { this.name = theName; } } new Animal("Cat").name; // Error: 'name' is private;
TypeScript使用的是结构性类型系统。 当我们比较两种不同的类型时,并不在乎它们从何处而来,如果所有成员的类型都是兼容的,我们就认为它们的类型是兼容的。
然而,当我们比较带有private或protected成员的类型的时候,情况就不同了。 如果其中一个类型里包含一个 private成员,那么只有当另外一个类型中也存在这样一个private成员, 并且它们都是来自同一处声明时,我们才认为这两个类型是兼容的。 对于 protected成员也使用这个规则。
下面来看一个例子,更好地说明了这一点:
class Animal { private name: string; constructor(theName: string) { this.name = theName; }<font></font> }<font></font> <font></font> class Rhino extends Animal { constructor() { super("Rhino"); }<font></font> }<font></font> <font></font> class Employee { private name: string; constructor(theName: string) { this.name = theName; }<font></font> }<font></font> <font></font> let animal = new Animal("Goat"); let rhino = new Rhino(); let employee = new Employee("Bob");<font></font> <font></font> animal = rhino;<font></font> animal = employee; // Error: Animal and Employee are not compatible
这个例子中有Animal
和Rhino
两个类,Rhino
是Animal
类的子类。 还有一个 Employee类,其类型看上去与Animal是相同的。 我们创建了几个这些类的实例,并相互赋值来看看会发生什么。 因为 Animal
和Rhino
共享了来自Animal里的私有成员定义private name: string
,因此它们是兼容的。 然而
Employee却不是这样。当把Employee
赋值给Animal
的时候,得到一个错误,说它们的类型不兼容。 尽管 Employee
里也有一个私有成员name,但它明显不是Animal
里面定义的那个。
理解protected
protected修饰符与private修饰符的行为很相似,但有一点不同,protected成员在派生类中仍然可以访问。例如:
class Person { protected name: string; constructor(name: string) { this.name = name; }<font></font> }<font></font> <font></font> class Employee extends Person { private department: string;<font></font> <font></font> constructor(name: string, department: string) { super(name) this.department = department;<font></font> }<font></font> <font></font> public getElevatorPitch() { return `Hello, my name is ${this.name} and I work in ${this.department}.`;<font></font> }<font></font> }<font></font> <font></font> let howard = new Employee("Howard", "Sales"); console.log(howard.getElevatorPitch()); console.log(howard.name); // error
注意,我们不能在Person类外使用name,但是我们仍然可以通过Employee类的实例方法访问,因为Employee是由Person派生而来的。
构造函数也可以被标记成protected。 这意味着这个类不能在包含它的类外被实例化,但是能被继承。比如,
class Person { protected name: string; protected constructor(theName: string) { this.name = theName; }<font></font> }<font></font> <font></font> // Employee can extend Person class Employee extends Person { private department: string;<font></font> <font></font> constructor(name: string, department: string) { super(name); this.department = department;<font></font> }<font></font> <font></font> public getElevatorPitch() { return `Hello, my name is ${this.name} and I work in ${this.department}.`;<font></font> }<font></font> }<font></font> <font></font> let howard = new Employee("Howard", "Sales"); let john = new Person("John"); // Error: The 'Person' constructor is protected
readonly修饰符
你可以使用readonly关键字将属性设置为只读的。 只读属性必须在声明时或构造函数里被初始化。
class Octopus {<font></font> readonly name: string;<font></font> readonly numberOfLegs: number = 8; constructor (theName: string) { this.name = theName;<font></font> }<font></font> }<font></font> let dad = new Octopus("Man with the 8 strong legs");<font></font> dad.name = "Man with the 3-piece suit"; // error! name is readonly.
后言
创作不易,要是本文章对广大读者有那么一点点帮助 不妨三连支持一下,您的鼓励就是博主创作的动力