Appearance
基础篇-类
TypeScript 中的类与 ES2015 几乎是一样的,主要区别是在于类成员的访问修饰符
ES6 中类的用法
属性和方法
使用 class 定义类,使用 constructor 定义构造函数。 通过 new 生成新实例的时候,会自动调用构造函数。
js
class Animal {
constructor(name) {
this.name = name;
}
sayHi() {
return `My name is ${this.name}`;
}
}
let a = new Animal('Jack');
console.log(a.sayHi()); // My name is Jack
类的继承
使用 extends 关键字实现继承,子类中使用 super 关键字来调用父类的构造函数和方法。
js
class Cat extends Animal {
constructor(name) {
super(name); // 调用父类的 constructor(name)
console.log(this.name);
}
sayHi() {
return 'Meow, ' + super.sayHi(); // 调用父类的 sayHi()
}
}
let c = new Cat('Tom'); // Tom
console.log(c.sayHi()); // Meow, My name is Tom
存取器
使用 getter 和 setter 可以改变属性的赋值和读取行为
js
class Animal {
constructor(name) {
this.name = name;
}
get name() {
return 'Jack';
}
set name(value) {
console.log('setter: ' + value);
}
}
let a = new Animal('Kitty'); // setter: Kitty
a.name = 'Tom'; // setter: Tom
console.log(a.name); // Jack
静态方法
使用 static 修饰符修饰的方法称为静态方法,它们不需要实例化,而是直接通过类来调用
js
class Animal {
static isAnimal(a) {
return a instanceof Animal;
}
}
let a = new Animal('Jack');
Animal.isAnimal(a); // true
a.isAnimal(a); // TypeError: a.isAnimal is not a function
ES7 中类的用法
实例属性
ES6 中实例的属性只能通过构造函数中的 this.xxx 来定义,ES7 提案中可以直接在类里面定义
js
class Animal {
name = 'Jack';
constructor() {
// ...
}
}
let a = new Animal();
console.log(a.name); // Jack
静态属性
ES7 提案中,可以使用 static 定义一个静态属性
js
class Animal {
static num = 42;
constructor() {
// ...
}
}
console.log(Animal.num); // 42
TS 中类的用法
TypeScript 可以使用三种访问修饰符(Access Modifiers),分别是 public、private 和 protected
- public 修饰的属性或方法是公有的,可以在任何地方被访问到,默认所有的属性和方法都是 public 的
- private 修饰的属性或方法是私有的,不能在声明它的类的外部访问
- protected 修饰的属性或方法是受保护的,它和 private 类似,区别是它在子类中也是允许被访问的
- readonly 只读属性必须在声明时或构造函数里被初始化
public
public 访问修饰符
在类里面、 子类 、类外面都可以访问
js
class Person {
constructor(name: string) {
}
}
const user = new Person('王');
console.log(user.name) //类型“Person”上不存在属性“name”
//可以在类上挂载
class Person {
name:string;
constructor(name: string) {
this.name=name
}
}
const user = new Person('王');
console.log(user.name) //王
//public 可以在类上挂载
class Person {
constructor(public name: string) {
}
}
const user = new Person('王');
console.log(user.name) //王
上面的例子中,name 被设置为了 public,所以直接访问实例的 name 属性是允许的。
很多时候,我们希望有的属性是无法直接存取的,这时候就可以用 private 了:
private
private 修饰符
在类里面可以访问,子类、类外部都没法访问
当成员被标记成 private 时,就不能在声明它的类的外部访问
js
class Person {
private name: string;
public constructor(name: string) {
this.name = name;
}
getName(): string {
return this.name;
}
setName(name: string): void {
this.name = name;
}
}
const user = new Person('王');
user.setName('李');
console.log(user.name); //private 直接访问实例的name 报错
console.log(user.getName());
使用 private 修饰的属性或方法,在子类中也是不允许访问的
js
class Xiao extends Person {
constructor(name: string) {
super(name);
console.log(this.name); //报错
}
}
对构造函数加上 private,则这个类既不能实例化也不可以继承
js
class Person {
private name: string;
private constructor(name: string) {
this.name = name;
}
getName(): string {
return this.name;
}
setName(name: string): void {
this.name = name;
}
}
const user = new Person("王"); //报错
class Xiao extends Person {}//报错
protected
protected 访问修饰符
在类里面、子类里面可以访问 ,实例化没法访问
如果是用 protected 修饰,则允许在子类中访问:
js
class Person {
protected name: string;
public constructor(name: string) {
this.name = name;
}
getName(): string {
return this.name;
}
setName(name: string): void {
this.name = name;
}
}
class Xiao extends Person {
constructor(name: string) {
super(name);
console.log(this.name); //子类中可以访问
}
}
let xiaoming = new Xiao('小明'); //小明
console.log(xiaoming.name); //实例中报错
console.log(xiaoming.getName()); //小明
readonly
readonly 修饰符
js
class Animal {
readonly name = '老外';
public constructor(name) {
this.name = name;
}
}
let cat = new Animal('mao');
cat.name = '老毛'; //报错
static
只能通过类或子类访问,实例无法访问
js
class Animal {
public name: string = "老外";
public constructor(name: string) {
this.name = name;
}
static age: number = 12;
}
let cat = new Animal("mao");
console.log(Animal.age);//类访问
console.log(cat.age); //实例无法访问
参数属性
参数属性通过给构造函数参数添加一个访问限定符来声明。 使用 private 限定一个参数属性会声明并初始化一个私有成员;对于 public 和 protected 来说也是一样。
js
class Animal {
public constructor(name: string) {}
static age: number = 12;
}
let cat = new Animal("mao");
console.log(cat);//Animal {}
在构造函数加修饰符
js
class Animal {
public constructor(public name: string) {}
static age: number = 12;
}
let cat = new Animal("mao");
console.log(cat);//Animal {name: "mao"}
abstract 抽象类
抽象类
abstract 用于定义抽象类和其中的抽象方法,有利于代码复用和扩展。 什么是抽象类 首先,抽象类只能被继承不能被实例化的类:
js
abstract class Animal {
public name;
public constructor(name) {
this.name = name;
}
public abstract sayHi();
}
let a = new Animal('Jack');
// index.ts(9,11): error TS2511: Cannot create an instance of the abstract class 'Animal'.
上面的例子中,我们定义了一个抽象类 Animal,并且定义了一个抽象方法 sayHi。在实例化抽象类的时候报错了。 其次,抽象类中的抽象方法必须被子类继承实现:
js
abstract class Animal {
public name;
public constructor(name) {
this.name = name;
}
public abstract sayHi();
}
class Cat extends Animal {
public eat() {
console.log(`${this.name} is eating.`);
}
}
let cat = new Cat('Tom');
// index.ts(9,7): error TS2515: Non-abstract class 'Cat' does not implement inherited abstract member 'sayHi' from class 'Animal'.
上面的例子中,我们定义了一个类 Cat 继承了抽象类 Animal,但是没有实现抽象方法 sayHi,所以编译报错了。 下面是一个正确使用抽象类的例子
js
abstract class Animal {
public name;
public constructor(name) {
this.name = name;
}
public abstract sayHi();
}
class Cat extends Animal {
public sayHi() {
console.log(`Meow, My name is ${this.name}`);
}
}
let cat = new Cat('Tom');
上面的例子中,我们实现了抽象方法 sayHi,编译通过了
类的类型
给类加上 TypeScript 的类型很简单,与接口类似
js
class Animal {
name: string;
constructor(name: string) {
this.name = name;
}
sayHi(): string {
return `My name is ${this.name}`;
}
}
let dog: Animal = new Animal('Jack');
console.log(dog.sayHi()); // My name is Jack