TS中的类
- js中的类的演变史
- 类的概念
- 类在ts中如何进行类型声明
- ES6中类的用法
- 属性方法
- 类的继承
- 类的存取器
- 静态方法
- ES7中类的用法
- 实例属性
- 静态属性
- Typescript中类的用法
- public 修饰的属性或方法是公有的,默认所有的属性和方法都是 public 的
- private 修饰的属性或方法是私有的,不能在声明它的类的外部访问
- protected 修饰的属性或方法是受保护的,它和 private 类似,区别是它在子类中也是允许被访问的
- constructor标记被private标记,既不能被实例化,也不能被继承
- 当构造函数修饰为 protected 时,该类只允许被继承不可以被实例化(继承后的类可以实例化)
- 参数属性
- 写法简化
- 只读属性
- 抽象类
- 抽象类不可以实例化
- 抽象类中的抽象方法必须被子类实现
- 类的类型
js中的类的演变史
类的概念在js早起是并不存在,所以实现面向对象编程是比较困难的,那个阶段称之为刀耕火种吧。
简单概括一下:传统方法中,JavaScript 通过工厂函数,构造函数 实现类的概念,通过原型链实现继承。
在 ES6 中,我们终于迎来了 class。
TypeScript 除了实现了所有 ES6 中的类的功能以外,还添加了一些新的用法。
演变史参看这篇文章
类的概念
概念写在前,提前记
- 类(Class):定义了一件事物的抽象特点,包含它的属性和方法
- 对象(Object):类的实例,通过 new 生成
- 面向对象(OOP)的三大特性:封装、继承、多态
- 封装(Encapsulation):将对数据的操作细节隐藏起来,只暴露对外的接口。外界调用端不需要(也不可能)知道细节,就能通过对外提供的接口来访问该对象,同时也保证了外界无法任意更改对象内部的数据
- 继承(Inheritance):子类继承父类,子类除了拥有父类的所有特性外,还有一些更具体的特性
- 多态(Polymorphism):由继承而产生了相关的不同的类,对同一个方法可以有不同的响应。比如 Cat 和 Dog 都继承自 Animal,但是分别实现了自己的 eat 方法。此时针对某一个实例,我们无需了解它是 Cat 还是 Dog,就可以直接调用 eat 方法,程序会自动判断出来应该如何执行 eat(这个eat可以是继承过来的,同时子类也可以对这个方法进行重写)。
- 存取器(getter & setter):用以改变属性的读取和赋值行为
- 修饰符(Modifiers):修饰符是一些关键字,用于限定成员或类型的性质。比如 public 表示公有属性或方法
- 抽象类(Abstract Class):抽象类是供其他类继承的基类,抽象类不允许被实例化。抽象类中的抽象方法必须在子类中被实现
- 接口(Interfaces):不同类之间公有的属性或方法,可以抽象成一个接口。接口可以被类实现(implements)。一个类只能继承自另一个类,但是可以实现多个接口
类在ts中如何进行类型声明
先熟悉知道这种写法,后面写的时候规范写
举个demo:
class Animal {
name: string;
constructor(name: string) {
this.name = name;
}
sayHi() {
return `My name is ${this.name}`
}
}
let mq: Animal = new Animal('Jack')
console.log(mq.sayHi())
编译后:
"use strict";
class Animal {
constructor(name) {
this.name = name;
}
sayHi() {
return `My name is ${this.name}`;
}
}
let mq = new Animal('Jack');
console.log(mq.sayHi());
ES6中类的用法
属性方法
1、使用class定义类,使用constructor定义构造函数
2、通过new生成新实例的时候,会自动调用构造函数
class Animal {
name: string;
constructor(name: string) {
this.name = name;
}
sayHi() {
return `My name is ${this.name}`
}
}
let mq: Animal = new Animal('Jack')
console.log(mq.sayHi()) // My name is Jack
类的继承
0、类的继承
1、尝试去打印super,但是报错:‘super’ keyword unexpected here
2、可以看到,继承的话,在父类中已有的方法,可以在子类中重写,Lion 就重写了Animal 的sayHi方法
3、super在子类的constructor函数中调用,可以直接当做是父类的constructor方法使用
4、super在子类的其余地方调用,可以理解为父类的实例对象,可以调用父类的方法
class Animal {
name: string;
constructor(name: string) {
this.name = name;
}
sayHi() {
return `My name is ${this.name}`
}
}
let mq: Animal = new Animal('Jack')
console.log(mq.sayHi()) // My name is Jack
class Lion extends Animal {
constructor(name: string) {
super(name)
}
sayHi() {
// console.log(super)
return `吼,` + super.sayHi()
}
}
let lio: Lion = new Lion('狮子')
console.log(lio.sayHi()) // 吼,My name is 狮子
类的存取器
仔细看存取器,存取器可不是方法,不能供实例调用的,实例去调用会报错的
class Tiger {
public _name: string
constructor(name: string) {
this._name = name
}
get name() {
return this._name
}
set name(name: string) {
this._name = name
}
}
let tiger = new Tiger('老虎')
console.log(tiger._name) // 老虎
tiger._name = '狐狸'
console.log(tiger._name) // 狐狸
// tiger.set('dog'); // 会报错的
console.log(tiger._name) // 狐狸
静态方法
类的静态方法在类上,类可以调用;实例是不行的
class Dolphin {
static swim(obj: object) {
return obj instanceof Dolphin;
}
}
let dolphin = new Dolphin();
console.log(dolphin) // {}
let result = Dolphin.swim(dolphin)
console.log(result) // true
dolphin.swim(dolphin) // 直接报错了:dolphin.swim is not a function
ES7中类的用法
es7中有一些关于类的提案,ts也实现了他们
实例属性
es6中实例属性智能通过this.xxx来定义,es7中提案中可以直接在类里面定义
class Apple {
name = 'apple';
constructor () {
}
}
let apple = new Apple()
console.log(apple.name) // apple
静态属性
class Peach {
static color = "pink";
constructor() {
}
}
console.log(Peach.color) // “pink”
Typescript中类的用法
TypeScript 可以使用三种访问修饰符(Access Modifiers),分别是 public、private 和 protected
public 修饰的属性或方法是公有的,默认所有的属性和方法都是 public 的
name 被设置为了 public,所以直接访问实例的 name 属性是允许的
class Banana {
public name;
constructor (name: string) {
this.name = name
}
}
const banana = new Banana('xiangjiao')
console.log(Banana.name) // Banana--类的名字
console.log(banana.name) // xiangjiao
banana.name = "xj"
console.log(banana.name) // xj
class Xj extends Banana {
constructor(name: string) {
super(name)
}
}
const xxjj = new Xj('香蕉')
console.log(xxjj.name) // 香蕉
console.log(Xj.name) // Xj--类的名字
private 修饰的属性或方法是私有的,不能在声明它的类的外部访问
有些时候,我们希望有的属性是无法直接存取的,这时候就可以用 private 了
class Watermelon {
private name;
constructor(name: string) {
this.name = name
}
}
let w = new Watermelon('xigua')
console.log(w.name) // Property 'name' is private and only accessible within class 'Watermelon'.
w.name = '西瓜' // Property 'name' is private and only accessible within class 'Watermelon'.
上述代码编译后:
class Watermelon {
constructor(name) {
this.name = name;
}
}
let w = new Watermelon('xigua');
console.log(w.name); // xigua
w.name = '西瓜';
console.log(w.name); // 西瓜
通过编译后的代码可以得出结论:TypeScript 编译之后的代码中,并没有限制 private 属性在外部的可访问性,private 仅在ts编译阶段报错,最终js的执行并不会报错。
private 修饰的属性在子类的中也是不允许访问的(同样不会影响编译后的js运行)
class Watermelon {
private name;
constructor(name: string) {
this.name = name
}
}
class Smallwatermelon extends Watermelon {
constructor(name: string) {
super(name)
// console.log(this.name) Property 'name' is private and only accessible within class 'Watermelon'.
}
}
const k = new Smallwatermelon('xigua1')
console.log(k.name) // Property 'name' is private and only accessible within class 'Watermelon'.
// -----编译后的代码-----
class Watermelon {
constructor(name) {
this.name = name;
}
}
class Smallwatermelon extends Watermelon {
constructor(name) {
super(name);
}
}
const k = new Smallwatermelon('xigua1');
console.log(k.name); // xigua1
protected 修饰的属性或方法是受保护的,它和 private 类似,区别是它在子类中也是允许被访问的
class Book {
protected name;
constructor(name:string) {
this.name = name
}
}
const money = new Book('小狗钱钱')
// console.log(money.name) // Property 'name' is protected and only accessible within class 'Book' and its subclasses.
class Xiyouji extends Book {
constructor(name:string) {
super('西游记')
console.log(this.name) // 不会报错
}
}
const xyj = new Xiyouji('西游记')
// console.log(xyj.name) // Property 'name' is protected and only accessible within class 'Book' and its subclasses
constructor标记被private标记,既不能被实例化,也不能被继承
class Kate {
public name;
private constructor(name: string) {
this.name = name
}
}
class Jake extends Kate { // Cannot extend a class 'Kate'. Class constructor is marked as private.
constructor(name: string) {
super(name)
}
}
let lade = new Kate('lade') // Constructor of class 'Kate' is private and only accessible within the class declaration
//----编译后的代码(可以看到编译后,其实是不受这个影响的)---
class Kate {
constructor(name) {
this.name = name;
}
}
class Jake extends Kate {
constructor(name) {
super(name);
}
}
let lade = new Kate('lade');
当构造函数修饰为 protected 时,该类只允许被继承不可以被实例化(继承后的类可以实例化)
class Kate {
public name;
protected constructor(name: string) {
this.name = name
}
}
class Jake extends Kate {
constructor(name: string) {
super(name)
}
}
let uu = new Jake('jake') // 继承来的类是可以实例化的
console.log(uu.name) // jake
let lade = new Kate('lade') // Constructor of class 'Kate' is protected and only accessible within the class declaration.
参数属性
写法简化
修饰符和readonly还可以使用在构造函数参数中,等同于类中定义该属性同时给该属性赋值,使代码更简洁。
class Zkf {
// public name;
public constructor(public name: string) {
// this.name = name
}
}
编译过后的js如下:
class Zkf {
// public name;
constructor(name) {
this.name = name;
// this.name = name
}
}
只读属性
只读属性关键字,只允许出现在属性声明或索引签名或构造函数中
class Furniture {
readonly name;
public constructor(name:string) {
this.name = name
}
}
let x = new Furniture('desk')
console.log(x.name) // desk
x.name = 'floor' // 报错:Cannot assign to 'name' because it is a read-only property.
编译后的js文件
class Furniture {
constructor(name) {
this.name = name;
}
}
let x = new Furniture('desk');
console.log(x.name); // desk
x.name = 'floor';
console.log(x.name); // floor
注意如果 readonly 和其他访问修饰符同时存在的话,需要写在其后面。
class Botany {
public constructor(public readonly name: any) {
}
}
抽象类
abstract 用于定义抽象类和其中的抽象方法
抽象类不可以实例化
abstract class Food {
public name;
public constructor(name:string) {
this.name = name
}
}
let rice = new Food('rice') // Cannot create an instance of an abstract class
// 编译后的js
class Food {
constructor(name) {
this.name = name;
}
}
let rice = new Food('rice');
抽象类中的抽象方法必须被子类实现
abstract class Food {
public name;
public constructor(name:string) {
this.name = name
}
public abstract sayHi(): any;
}
class Mianbao extends Food{
public eat() {
console.log(`${this.name} is eat`)
}
}
// 编译报错: Non-abstract class 'Mianbao' does not implement all abstract members of 'Food'
// 如下便不会报错
abstract class Food {
public name;
public constructor(name:string) {
this.name = name
}
public abstract sayHi(): any;
}
class Mianbao extends Food{
public eat() {
console.log(`${this.name} is eat`)
}
public sayHi() {
console.log('sayhi')
}
}
正确demo:
abstract class Food {
public name;
public constructor(name:string) {
this.name = name
}
public abstract sayHi(): any;
}
class Mianbao extends Food{
public eat() {
console.log(`${this.name} is eat`)
}
public sayHi() {
console.log(`${this.name} is my name`)
}
}
let ff = new Mianbao('面包')
console.log(ff.name) // 面包
类的类型
class Vehicle {
name: string;
constructor(name: string) {
this.name = name
}
sayHi(): string {
return `my name is ${this.name}`
}
}
let bic: Vehicle = new Vehicle('bicycle')
console.log(bic.sayHi()) // my name is bicycle