类的概念在js早起是并不存在,所以实现面向对象编程是比较困难的,那个阶段称之为刀耕火种吧。
简单概括一下:传统方法中,JavaScript 通过工厂函数,构造函数 实现类的概念,通过原型链实现继承。
在 ES6 中,我们终于迎来了 class。
TypeScript 除了实现了所有 ES6 中的类的功能以外,还添加了一些新的用法。
演变史参看这篇文章
概念写在前,提前记
先熟悉知道这种写法,后面写的时候规范写
举个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());
详细参阅阮一峰大佬的文章
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中有一些关于类的提案,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 可以使用三种访问修饰符(Access Modifiers),分别是 public、private 和 protected
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 了
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
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
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');
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