概述
早期的 JavaScript 使用函数和基于原型的继承来创建「可重用组件」,但对于 Java 等面向对象编程语言开发者而言,这种方式有些认知差异,因为在 Java 等语言中,是基于类的继承并且由类来创建对象。从 ES6 开始,JavaScript 开始支持 类 这个面向对象编程特性。而在 TypeScript 中,默认就支持面向对象,编译后的 JavaScript 代码可以在所有主流浏览器和平台上运行,不需要 JavaScript 的下个版本。
TypeScript 中的类和 Java 等语言非常相似,有面向对象编程经历的开发者很快就能熟悉并上手。
类的语法
下面来学习下 TypeScript 中类的具体语法。
TypeScript 中使用 class 关键字定义一个类,后面则是类名,跟在最后是一对花括号,花括号中就是类的作用域:
class 类名 {
}
在类的作用域中包含以下几个元素:
- 成员变量(属性/字段):类里声明的变量,表示对象的相关属性。
- 成员方法:对象要执行的操作。
- 构造函数:用来实例化一个类的对象,为对象分配内存。
实例化一个类的对象时,使用 new 关键字。
var/let/const 对象名 = new 类名([参数列表]);
// 比如
let xiaoming = new Person("xiaoming");
访问类中的属性和方法可以使用 .:
对象名.属性名
对象名.方法名()
// 比如
xiaoming.name = "xiaoming";
xiaoming.sayHello();
看一个完整的简单例子:
class Person {
name: string;
constructor(name: string) {
this.name = name;
}
sayHello() {
console.log("Hello, my name is " + this.name);
}
}
let xiaoming = new Person("xiaoming");
xiaoming.sayHello(); // 输出:Hello, my name is xiaoming
在类中定义属性,前面不用加 var 或 let 关键字;
在类中定义方法,前面不用加 function 关键字。
上面代码展现了类的典型用法,以及成员变量,成员方法、构造函数等特性,它编译成 JavaScript 后是这样的:
var Person = /** @class */ (function () {
function Person(name) {
this.name = name;
}
Person.prototype.sayHello = function () {
console.log("Hello, my name is" + this.name);
};
return Person;
}());
var xiaoming = new Person("xiaoming");
xiaoming.sayHello();
对于 Java 程序员来说一下子就懵逼了。这都是啥啥啥。。。
访问控制修饰符
TypeScript 中,可以给类的属性、方法、构造函数加上访问控制修饰符来控制其他类是否能访问这些元素,它是面向对象封装特性的体现。TypeScript 支持 3 种不同的访问控制修饰符。
- public:公开的,默认修饰符,不写就是 public,可以在任何地方被访问。
- protected:受保护的,可以在类自身和它的子类中访问。
- private:私有的,只能在这个类自己内部访问,子类也无法访问。
例如:
class Person {
name: string; // public
protected age: number; // protected
private height: number; // private
}
let person = Person();
person.name = "xiaoming"; // OK
person.age = 12; // Error: 属性“age”受保护,只能在类“Person”及其子类中访问。
person.height = 140; // Error: 属性“height”为私有属性,只能在类“Person”中访问。
类的继承
在面向对象编程中,一个类可以继承另一个类,继承的类称为子类(派生类),被继承的类称为父类(基类)。在 TypeScript 中,类继承关系用 extends 关键字表示。
class 子类 extends 父类 {}
// 比如
class Person {}
class Student extends Person {}
- 子类中可以定义自己的属性和方法,还可以访问父类中非
private的属性和方法
例如,我们有前面定义的 Person 类作为父类。再定义一个 Student 作为子类。
class Student extends Person {
score: number;
}
let xiaoming = new Student("xiaoming");
console.log(xiaoming.name); // 输出:xiaoming
console.log(xiaoming.score); // 输出:undefined
xiaoming.sayHello(); // 输出:Hello, my name is xiaoming
- 子类中可以对父类方法实现进行覆盖。使用
super关键字引用父类对象。
class Person {
name: string;
constructor(name: string) {
this.name = name;
}
sayHello() {
console.log("Hello, my name is " + this.name);
}
}
class Student extends Person {
score: number;
sayHello() {
super.sayHello();
console.log("My score is " + this.score);
}
}
let xiaoming = new Student("xiaoming");
xiaoming.score = 90;
xiaoming.sayHello();
// 输出:
Hello, my name is xiaoming
My score is 90
继承的代码编译成 JavaScript 后是这样:
var Person = /** @class */ (function () {
function Person(name) {
this.name = name;
}
Person.prototype.sayHello = function () {
console.log("Hello, my name is " + this.name);
};
return Person;
}());
var Student = /** @class */ (function (_super) {
__extends(Student, _super);
function Student() {
return _super !== null && _super.apply(this, arguments) || this;
}
Student.prototype.sayHello = function () {
_super.prototype.sayHello.call(this);
console.log("My score is " + this.score);
};
return Student;
}(Person));
- TypeScript 不支持继承多个父类,但可以多重继承,可以使用
implements关键字实现多个接口。
class Person {}
class Student extends Person {}
interface Play {
likedSports: string[];
doSports(): void;
}
class HighSchoolStudent extends Student implements Play {
likedSports: string[];
doSports(): void {
console.log("Do sports");
}
}
上面代码中,HighSchoolStudent 继承自 Student,Student 继承自 Person,这是多重继承,同时 HighSchoolStudent 实现了 Play 接口。
static 关键字
static 关键字用来定义类级别的静态的成员,包括属性和方法,静态成员可以用 类名.静态成员 的方式直接访问。
class StaticMember {
static num: number;
static printNum(): void {
console.log("num = " + StaticMember.num)
}
}
StaticMember.num = 10;
StaticMember.printNum();
instanceof 关键字
可以使用 instanceof 运算符判断对象是否是指定的类型,如果是,返回 true,如果不是,返回 false。
class Person {
name: string;
constructor(name: string) {
this.name = name;
}
sayHello() {
console.log("Hello, my name is " + this.name);
}
}
let person = new Person("owen");
console.log("typeof person = " + (typeof person));
console.log("person instanceof Person = " + (person instanceof Person));
// 运行结果
typeof person = object
person instanceof Person = true
抽象类
抽象类是作为其他子类的父类,约束子类一定要有某些属性或方法,它一般不会直接实例化。和接口不同,抽象类可以包含具体实现。使用 abstract 关键字定义抽象类及抽象类中的抽象方法。抽象类中的抽象方法没有实现,必须要在子类中实现。abstract 关键字不能和 private 关键字一起使用。
abstract class Person {
abstract name: string; // 抽象属性,和接口的属性一致,需要在子类中再次声明
sayHello() { // 普通函数,抽象类也可以有包含具体实现的普通函数
console.log("Hello, my name is " + this.name);
}
abstract walk(): void; // 抽象方法,必须要在子类中实现
}
class Student extends Person {
name: string;
constructor(name: string, score: number) {
super(); // 子类必须要调用抽象类的构造函数
this.name = name;
this.score = score;
}
walk(): void { // 子类实现父类的抽象方法
console.log("Student walk");
}
score: number;
sayHello() {
super.sayHello();
console.log("My score is " + this.score);
}
}
let xiaoming = new Student("xiaoming", 90);
xiaoming.sayHello();
xiaoming.walk();
// 运行结果
Hello, my name is xiaoming
My score is 90
Student walk
开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 9 天,点击查看活动详情