Class(类)分享 第四节
一、基础
// es6 普通的一个类
// 构造函数为实例添加成员,类的所有方法都定义在类的prototype属性,即原型上面。
class Animal {
name
constructor(name, age){
this.name = name
}
move(distance){
console.log(`The speed of ${this.name} is ${distance}m/s`)
}
}
let a = new Animal("Tom")
console.log(a.move(5))
//ts, 成员,参数,函数返回值加上类型检查,实例加上类类型
class Animal { // 声明了类的实例的类型
name: string
constructor(name: string){
this.name = name
}
move(distance: number): void{
console.log(`The speed of ${this.name} is ${distance}m/s`)
}
}
let a: Animal = new Animal("Tom")
console.log(a.move(5))
二、继承
使用extends
关键字实现继承。子类中如果有构造方法,必须使用super
关键字来调用父类的构造函数
class Animal {
name: string;
age: number;
constructor(name: string, age: number) {
this.name = name
this.age = age
}
move(distance: number): void {
console.log(`The speed of ${this.name} is ${distance}m/s`)
}
}
class Cat extends Animal {
catkind
// 子类有构造函数,必须使用super调用父类构造方法
constructor(name: string, age: number, catkind: string) {
super(name, age)
this.catkind = catkind
}
// 重写move
move(distance: number): void {
console.log("cat walking...");
super.move(distance)
}
}
class Dog extends Animal {
dogkind
constructor(name: string, age: number, dogkind: string) {
super(name, age)
this.dogkind = dogkind
}
move(distance: number): void {
console.log("dog walking...");
super.move(distance)
}
}
let cat1: Cat = new Cat("Tom", 5, "persian")
console.log(`${cat1.name} is ${cat1.age} years old,its kind is ${cat1.catkind} cat`)
console.log(cat1.move(5))
let dog1: Dog = new Dog("jack", 3, "Husky")
console.log(`${dog1.name} is ${dog1.age} years old,its kind is ${dog1.dogkind} dog`)
console.log(dog1.move(10))
三、public private 和 protected
- public:默认所有的成员和方法都是public。修饰的成员和方法是公开的,可以在任何地方被访问到
- private: 修饰的成员和方法是私有的的,只能在声明它的类的内部被访问, 不允许被实例和子类访问
- protected:修饰的成员和方法是受保护的,只能在声明它的类和子类的内部被访问, 不允许被实例访问
// 1. public
class Animal {
public name: string;
public constructor(name: string) {
this.name = name;
}
public move(distance: number): void {
console.log(`The speed of ${this.name} is ${distance}m/s。`)
}
}
let cat1 = new Animal("Tom")
console.log(cat1.name)
console.log(cat1.move(5))
// 2.1 private-成员
class Animal {
private name: string;
public constructor(name: string) {
this.name = name;
}
public move(distance: number): void {
console.log(`The speed of ${this.name} is ${distance}m/s。`)
}
}
let cat1 = new Animal('Tom');
console.log(cat1);
console.log(cat1.name); // error 实例不能访问
console.log(cat1.move(85)); // 类内部可以访问
class Cat extends Animal{
public constructor(name: string){
super(name);
}
public say() {
console.log(`${this.name} say miao~miao~`);
}
}
let cat2 = new Cat('haha');
console.log(cat2.name) // error 在子类的实例中也不能被访问
// 2.2 private-构造函数
// 构造函数修饰为 private 时,该类不允许被继承或者实例化
class Animal {
public name;
private constructor(name) {
this.name = name;
}
}
class Cat extends Animal { // error a. 不能被继承
constructor(name) {
super(name);
}
}
let cat1 = new Animal('Jack'); // error b. 不能被实例化
// 3.1 protected-成员
class Animal {
protected name: string;
public constructor(name: string) {
this.name = name;
}
public move(distance: number): void {
console.log(`The speed of ${this.name} is ${distance}m/s。`)
}
}
let cat1 = new Animal('Tom');
console.log(cat1.move(85)); // 类内部可以访问
console.log(cat1.name); // error 实例不能访问
class Cat extends Animal{
public constructor(name: string){
super(name);
}
public say() {
console.log(`${this.name} say miao~miao~`); // 在子类中可以访问
}
}
let cat2 = new Cat('haha');
console.log(cat2.say())
console.log(cat2.name) // error 在子类的实例中也不能被访问
// 3.2 protected-构造函数
// 构造函数修饰为 protected 时,该类只允许被继承
class Animal {
public name;
protected constructor(name) {
this.name = name;
}
}
class Cat extends Animal {
constructor(name) {
super(name);
}
}
let a = new Animal('Jack'); // error 不能被实例化
四、readonly
使用readonly
关键字将成员设置为只读的。 只读成员必须在声明时或构造函数里被初始化。
class Animal {
readonly name: string;
readonly age: number = 18; // 声明时初始化
public constructor(name: string) {
this.name = name; // 构造函数里初始化
}
}
let a = new Animal('Jack');
console.log(a.name); // Jack
a.name = 'Tom'; // error 只读
注意如果readonly
和其他访问修饰符同时存在的话,需要写在其后面。
class Animal {
public readonly name: string;
public constructor(name: string) {
this.name = name;
}
}
五、参数属性
一般,我们先声明类的成员和构造函数内赋值。通过参数属性,我们将声明和赋值合并起来。
// 一般
class Animal {
public name: string;
public constructor(name: string) {
this.name = name;
}
}
let a = new Animal('Jack');
console.log(a.name); // Jack
// 参数属性
class Animal {
// public name: string; // 省略
public constructor(public name: string) {
// 参数属性,必须有修饰符 public,protected,private或readonly
// this.name = name; // 可省略
}
}
let a = new Animal('Jack');
console.log(a.name); // Jack
六、存取器
使用 getter 和 setter 可以改变属性的赋值和读取行为。当类成员上定义存取器时,就不能使用正常成员声明
class Animal {
// name: string 成员声明
constructor(name: string) {
this.name = name;
}
get name() { // 存取器
return 'Jack';
}
set name(value) {
console.log('setter: ' + value);
}
}
let a = new Animal('Kitty');
a.name = 'Tom';
console.log(a.name);
七、静态属性
可以通过static定义创建类的静态成员和方法,这些成员和方法存在于类的本身而不是类的实例上。只能通过类名访问,不能被构造函数实例化
class Animal {
static firstWord = "hello";
constructor(public name: string){}
sayHi(){
console.log(Animal.firstWord)
}
static isAnimal(animal: Animal): void {
console.log(animal instanceof Animal);
}
}
let a = new Animal("Tom")
console.log(Animal.firstWord)
Animal.isAnimal(a)
console.log(a.firstWord) // error, 实例不能访问
a.isAnimal(a) // error
a.sayHi()
八、抽象类
abstract
用于定义抽象类和其中的抽象方法。顾名思义,抽象类和抽象方法是对对一些对象的属性和行为进行高度抽象,一般不包含具体实现细节,所以基类使用。所以,抽象类是不允许被实例化的。其次,抽象类中的抽象方法必须被子类实现,因为一般不包含实现细节。
abstract class Animal {
name: string;
constructor(name: string) {
this.name = name;
}
abstract move(distance: number): void;
}
class Cat extends Animal {
constructor(name: string) {
super(name)
}
move(distance: number): void { // 必须实现基类的抽象方法
console.log(`The speed of ${this.name} is ${distance}m/s`)
}
}
let a = new Animal('Jack'); // error,不允许实例化
let cat = new Cat('Tom');
cat.move(5)
九、类和接口
一般来讲,一个类只能继承自另一个类,有时候不同类之间可以有一些共有的属性和行为,这时候就可以把这些通用属性和方法提取成接口(interfaces),用implements
关键字来实现。
举例,门是一个类,防盗门是它的子类。车也是一个类,车和防盗门都有报警功能,将其提取成一个报警方法。作为一个接口,防盗门和车等都可以实现它。
interface Alarm {
alert(): void;
}
class Door {
}
class SecurityDoor extends Door implements Alarm {
alert() {
console.log('SecurityDoor alert');
}
}
class Car implements Alarm {
alert() {
console.log('Car alert');
}
}
一个类只能继承自一个类,但也可以实现多个接口
interface Alarm { // 报警
alert(): void;
}
interface controlDoor { // 开关门
openDoor(): void;
closeDoor(): void;
}
class Car implements Alarm, controlDoor {
alert() {
console.log('Car alert');
}
openDoor() {
console.log('Car door open');
}
closeDoor() {
console.log('Car door close');
}
}
类定义会创建两个东西:类的实例类型和一个构造函数。 因为类可以创建出类型,所以你能够在允许使用接口的地方使用类。
class Point {
x: number;
y: number;
}
interface Point3d extends Point { // 类可以被当作接口使用
z: number;
}
let point3d: Point3d = {x: 1, y: 2, z: 3};