类
1. 如何定义类
- "strictPropertyInitialization": true // 启用类属性初始化的严格检查
- name!:string
ts
class Person {
name: string;
getName(): void {
console.log(this.name);
}
}
let p1 = new Person();
p1.name = "hello ";
p1.getName();ts
/**
* 当我们写一个类的时候,会得到2个类型
* 1. 构造函数类型的函数类型
* 2. 类的实例类型
*/
class Component {
static myName: string = "静态名称属性";
myName: string = "实例名称属性";
}
let com = Component;
//Component类名本身表示的是实例的类型
//ts 一个类型 一个叫值
//冒号后面的是类型
//放在=后面的是值
let c: Component = new Component();
let f: typeof Component = com;2. 存取器
- 在 TypeScript 中,我们可以通过存取器来改变一个类中属性的读取和赋值行为
- 构造函数
- 主要用于初始化类的成员变量属性
- 类的对象创建时自动调用执行
- 没有返回值
ts
class User {
myname: string;
constructor(myname: string) {
this.myname = myname;
}
get name() {
return this.myname;
}
set name(value) {
this.myname = value;
}
}
let user = new User("hello ");
user.name = "jiagou";
console.log(user.name);ts
"use strict";
var User = /** @class */ (function () {
function User(myname) {
this.myname = myname;
}
Object.defineProperty(User.prototype, "name", {
get: function () {
return this.myname;
},
set: function (value) {
this.myname = value;
},
enumerable: true,
configurable: true,
});
return User;
})();
var user = new User("hello ");
user.name = "jiagou";
console.log(user.name);3. 参数属性
ts
class User {
constructor(public myname: string) {}
get name() {
return this.myname;
}
set name(value) {
this.myname = value;
}
}
let user = new User("hello ");
console.log(user.name);
user.name = "jiagou";
console.log(user.name);4. readonly
- readonly修饰的变量只能在构造函数中初始化
- 在 TypeScript 中,const 是常量标志符,其值不能被重新分配
- TypeScript 的类型系统同样也允许将 interface、type、 class 上的属性标识为 readonly
- readonly 实际上只是在编译阶段进行代码检查。而 const 则会在运行时检查(在支持 const 语法的 JavaScript 运行时环境中)
ts
class Animal {
public readonly name: string;
constructor(name: string) {
this.name = name;
}
changeName(name: string) {
this.name = name;
}
}
let a = new Animal("hello ");
a.changeName("xiaoming");5. 继承
- 子类继承父类后子类的实例就拥有了父类中的属性和方法,可以增强代码的可复用性
- 将子类公用的方法抽象出来放在父类中,自己的特殊逻辑放在子类中重写父类的逻辑
- super可以调用父类上的方法和属性
ts
class Person {
name: string; // 定义实例的属性,默认省略public修饰符
age: number;
constructor(name: string, age: number) {
// 构造函数
this.name = name;
this.age = age;
}
getName(): string {
return this.name;
}
setName(name: string): void {
this.name = name;
}
}
class Student extends Person {
no: number;
constructor(name: string, age: number, no: number) {
super(name, age);
this.no = no;
}
getNo(): number {
return this.no;
}
}
let s1 = new Student("hahaha ", 10, 1);
console.log(s1);6. 类里面的修饰符
ts
class Father {
public name: string; //类里面 子类 其它任何地方外边都可以访问
protected age: number; //类里面 子类 都可以访问,其它任何地方不能访问
private money: number; //类里面可以访问, 子类和其它任何地方都不可以访问
constructor(name: string, age: number, money: number) {
//构造函数
this.name = name;
this.age = age;
this.money = money;
}
getName(): string {
return this.name;
}
setName(name: string): void {
this.name = name;
}
}
class Child extends Father {
constructor(name: string, age: number, money: number) {
super(name, age, money);
}
desc() {
console.log(`${this.name} ${this.age} ${this.money}`);
}
}
let child = new Child("hahaha ", 10, 1000);
console.log(child.name);
console.log(child.age);
console.log(child.money);7. 静态属性 静态方法
ts
class Father {
static className = "Father";
static getClassName() {
return Father.className;
}
public name: string;
constructor(name: string) {
//构造函数
this.name = name;
}
}
console.log(Father.className);
console.log(Father.getClassName());8. 装饰器
- 装饰器是一种特殊类型的声明,它能够被附加到类声明、方法、属性或参数上,可以修改类的行为
- 常见的装饰器有类装饰器、属性装饰器、方法装饰器和参数装饰器
- 装饰器的写法分为普通装饰器和装饰器工厂
ts
class Person {
say() {
console.log("hello");
}
}
function Person() {}
Object.defineProperty(Person.prototype, "say", {
value: function () {
console.log("hello");
},
enumerable: false,
configurable: true,
writable: true,
});8.1 类装饰器
- 类装饰器在类声明之前声明,用来监视、修改或替换类定义
ts
namespace a {
//当装饰器作为修饰类的时候,会把构造器传递进去
function addNameEat(constructor: Function) {
constructor.prototype.name = "hello ";
constructor.prototype.eat = function () {
console.log("eat");
};
}
@addNameEat
class Person {
name!: string;
eat!: Function;
constructor() {}
}
let p: Person = new Person();
console.log(p.name);
p.eat();
}
namespace b {
//还可以使用装饰器工厂
function addNameEatFactory(name: string) {
return function (constructor: Function) {
constructor.prototype.name = name;
constructor.prototype.eat = function () {
console.log("eat");
};
};
}
@addNameEatFactory("hello ")
class Person {
name!: string;
eat!: Function;
constructor() {}
}
let p: Person = new Person();
console.log(p.name);
p.eat();
}
namespace c {
// 还可以替换类,不过替换的类要与原类结构相同
function enhancer(constructor: Function) {
return class {
name: string = "xiaoming";
eat() {
console.log("吃饭饭");
}
};
}
@enhancer
class Person {
name!: string;
eat!: Function;
constructor() {}
}
let p: Person = new Person();
console.log(p.name);
p.eat();
}8.2 属性装饰器
- 属性装饰器表达式会在运行时当作函数被调用,传入下列2个参数
- 属性装饰器用来装饰属性
- 第一个参数对于静态成员来说是类的构造函数,对于实例成员是类的原型对象
- 第二个参数是属性的名称
- 方法装饰器用来装饰方法
- 第一个参数对于静态成员来说是类的构造函数,对于实例成员是类的原型对象
- 第二个参数是方法的名称
- 第三个参数是方法描述符
ts
namespace d {
//修饰实例属性
function upperCase(target: any, propertyKey: string) {
let value = target[propertyKey];
const getter = function () {
return value;
};
// 用来替换的setter
const setter = function (newVal: string) {
value = newVal.toUpperCase();
};
// 替换属性,先删除原先的属性,再重新定义属性
if (delete target[propertyKey]) {
Object.defineProperty(target, propertyKey, {
get: getter,
set: setter,
enumerable: true,
configurable: true,
});
}
}
//修饰实例方法
function noEnumerable(
target: any,
property: string,
descriptor: PropertyDescriptor,
) {
console.log("target.getName", target.getName);
console.log("target.getAge", target.getAge);
descriptor.enumerable = true;
}
//重写方法
function toNumber(
target: any,
methodName: string,
descriptor: PropertyDescriptor,
) {
let oldMethod = descriptor.value;
descriptor.value = function (...args: any[]) {
args = args.map((item) => parseFloat(item));
return oldMethod.apply(this, args);
};
}
class Person {
@upperCase
name: string = "hello ";
public static age: number = 10;
constructor() {}
@noEnumerable
getName() {
console.log(this.name);
}
@toNumber
sum(...args: any[]) {
return args.reduce((accu: number, item: number) => accu + item, 0);
}
}
let p: Person = new Person();
for (let attr in p) {
console.log("attr=", attr);
}
p.name = "jiagou";
p.getName();
console.log(p.sum("1", "2", "3"));
}8.3 参数装饰器
- 会在运行时当作函数被调用,可以使用参数装饰器为类的原型增加一些元数据
- 第1个参数对于静态成员是类的构造函数,对于实例成员是类的原型对象
- 第2个参数的名称
- 第3个参数在函数列表中的索引
ts
namespace d {
interface Person {
age: number;
}
function addAge(target: any, methodName: string, paramsIndex: number) {
console.log(target);
console.log(methodName);
console.log(paramsIndex);
target.age = 10;
}
class Person {
login(username: string, @addAge password: string) {
console.log(this.age, username, password);
}
}
let p = new Person();
p.login("hello ", "123456");
}8.4 装饰器执行顺序
- 有多个参数装饰器时:从最后一个参数依次向前执行
- 方法和方法参数中参数装饰器先执行。
- 类装饰器总是最后执行
- 方法和属性装饰器,谁在前面谁先执行。因为参数属于方法一部分,所以参数会一直紧紧挨着方法执行
- 类比React组件的componentDidMount 先上后下、先内后外
ts
namespace e {
function Class1Decorator() {
return function (target: any) {
console.log("类1装饰器");
};
}
function Class2Decorator() {
return function (target: any) {
console.log("类2装饰器");
};
}
function MethodDecorator() {
return function (
target: any,
methodName: string,
descriptor: PropertyDescriptor,
) {
console.log("方法装饰器");
};
}
function Param1Decorator() {
return function (target: any, methodName: string, paramIndex: number) {
console.log("参数1装饰器");
};
}
function Param2Decorator() {
return function (target: any, methodName: string, paramIndex: number) {
console.log("参数2装饰器");
};
}
function PropertyDecorator(name: string) {
return function (target: any, propertyName: string) {
console.log(name + "属性装饰器");
};
}
@Class1Decorator()
@Class2Decorator()
class Person {
@PropertyDecorator("name")
name: string = "hello ";
@PropertyDecorator("age")
age: number = 10;
@MethodDecorator()
greet(@Param1Decorator() p1: string, @Param2Decorator() p2: string) {}
}
}
/**
name属性装饰器
age属性装饰器
参数2装饰器
参数1装饰器
方法装饰器
类2装饰器
类1装饰器
*/9. 抽象类
- 抽象描述一种抽象的概念,无法被实例化,只能被继承
- 无法创建抽象类的实例
- 抽象方法不能在抽象类中实现,只能在抽象类的具体子类中实现,而且必须实现
ts
abstract class Animal {
name!: string;
abstract speak(): void;
}
class Cat extends Animal {
speak() {
console.log("喵喵喵");
}
}
let animal = new Animal(); //Cannot create an instance of an abstract class
animal.speak();
let cat = new Cat();
cat.speak();| 访问控制修饰符 | private protected public |
|---|---|
| 只读属性 | readonly |
| 静态属性 | static |
| 抽象类、抽象方法 | abstract |
10. 抽象方法
- 抽象类和方法不包含具体实现,必须在子类中实现
- 抽象方法只能出现在抽象类中
- 子类可以对抽象类进行不同的实现
ts
abstract class Animal {
abstract speak(): void;
}
class Dog extends Animal {
speak() {
console.log("小狗汪汪汪");
}
}
class Cat extends Animal {
speak() {
console.log("小猫喵喵喵");
}
}
let dog = new Dog();
let cat = new Cat();
dog.speak();
cat.speak();11. 重写(override) vs 重载(overload)
- 重写是指子类重写继承自父类中的方法
- 重载是指为同一个函数提供多个类型定义
ts
class Animal {
speak(word: string): string {
return "动作叫:" + word;
}
}
class Cat extends Animal {
speak(word: string): string {
return "猫叫:" + word;
}
}
let cat = new Cat();
console.log(cat.speak("hello"));
//--------------------------------------------
function double(val: number): number;
function double(val: string): string;
function double(val: any): any {
if (typeof val == "number") {
return val * 2;
}
return val + val;
}
let r = double(1);
console.log(r);12. 继承 vs 多态
- 继承(Inheritance)子类继承父类,子类除了拥有父类的所有特性外,还有一些更具体的特性
- 多态(Polymorphism)由继承而产生了相关的不同的类,对同一个方法可以有不同的行为
ts
class Animal {
speak(word: string): string {
return "Animal: " + word;
}
}
class Cat extends Animal {
speak(word: string): string {
return "Cat:" + word;
}
}
class Dog extends Animal {
speak(word: string): string {
return "Dog:" + word;
}
}
let cat = new Cat();
console.log(cat.speak("hello"));
let dog = new Dog();
console.log(dog.speak("hello"));