🔥TS进阶之「装饰器」

装饰器

概述

面向对象的概念(java:注解,c#:特征),decorator

angular大量使用,react中也会用到

目前JS支持装饰器,目前处于建议征集的第三阶段

解决的问题

装饰器,能够带来额外的信息量,可以达到分离关注点的目的。

  • 信息书写位置的问题
  • 重复代码的问题

上述两个问题产生的根源:某些信息,在定义时,能够附加的信息量有限。

装饰器的作用:为某些属性、类、参数、方法提供元数据信息(metadata)

元数据:描述数据的数据

装饰器的本质

在JS中,装饰器是一个函数。(装饰器是要参与运行的)

装饰器可以修饰:

  • 成员(属性+方法)
  • 参数

类装饰器

类装饰器的本质是一个函数,该函数接收一个参数,表示类本身(构造函数本身)

使用装饰器@得到一个函数

在TS中,如何约束一个变量为类

  • Function
  • new (参数)=>object
ts 复制代码
// 第一种:
function test(target: Function){
    console.log(target)
}

// 第二种:
function test(target: new() => object){
    console.log(target)
}

@test
class A{}

运行结果:

装饰器函数的运行时间:在类定义后直接运行

在TS中要使用装饰器,需要开启experimentalDecorators

类装饰器可以具有的返回值:

  • void:仅运行函数
  • 返回一个新的类:会将新的类替换掉装饰目标
ts 复制代码
type constructor = new (...args: any[]) => object;

function d1(){
  console.log("d1")
  return function(target: constructor){
    console.log('d1 decorator')
  }
}
function d2(){
  console.log("d2")
  return function(target: constructor){
    console.log('d2 decorator')
  }
}

@d1()
@d2()
class A {
  num: number
}
const a = new A()

运行结果:

可以有多个装饰器修饰

成员装饰器

属性:

属性装饰器也是一个函数,该函数需要两个参数:

  1. 如果是静态属性,则为类本身;如果是实例属性,则为类的原型;
  2. 固定为一个字符串,表示属性名
ts 复制代码
function d(target: any, key: string){
  console.log(target, key)
}

class A {
  @d
  static prop1: string

  @d
  prop2: number
}

方法:

方法装饰器也是一个函数,该函数需要三个参数:

  1. 如果是静态方法,则为类本身;如果是实例方法,则为类的原型;
  2. 固定为一个字符串,表示方法名
  3. 属性描述对象
ts 复制代码
function enumerable(target: any, key: string, descriptor: PropertyDescriptor) {
    // console.log(target, key, descriptor);
    descriptor.enumerable = true;
}

function useless(target: any, key: string, descriptor: PropertyDescriptor) {
    descriptor.value = function () {
        console.warn(key + "方法已过期");
    }
}

class A{
  @enumerable
  @useless
  method1() {
    console.log("method1")
  }

  @enumerable
  method2() {
    console.log("method2")
  }
}
const a = new A()

可以有多个装饰器修饰

reflect-metadata库

该库的作用:保存元数据

封装一个工具函数:

ts 复制代码
const key = Symbol.for("descriptor");

export function descriptor(description: string) {
  return Reflect.metadata(key, description);
}

// 提供打印的功能
export function printObj(obj: any) {
  const cons = Object.getPrototypeOf(obj);
  if (Reflect.hasMetadata(key, cons)) {
    console.log(Reflect.getMetadata(key, cons));
  } else {
    console.log(cons.constructor.name);
  }

  // 输出所有的属性描述和属性值
  for (const k in obj) {
    if (Reflect.hasMetadata(key, obj, k)) {
      console.log(`\t${Reflect.getMetadata(key, obj, k)}:${obj[k]}`)
    }
    else{
      console.log(`\t${k}:${obj[k]}`)
    }
  }
}

页面使用:

ts 复制代码
import { descriptor } from './Descriptor'
import "reflect-metadata";

@Reflect.metadata("a", "一个类")
@Reflect.metadata("a1", "一个类A1")
@Reflect.metadata("a2", "一个类A2")

class A{
  @Reflect.metadata('prop', "一个属性")
  prop1: string
}

const obj = new A()
console.log(Reflect.getMetadata('a', A))
console.log(Reflect.getMetadata('a', Object.getPrototypeOf(obj).constructor))
console.log(Reflect.getMetadata('prop', obj,'prop1' ))

class-validator 库

ts 复制代码
import {IsNotEmpty, MinLength,MaxLength ,Min, Max,validate} from 'class-validator'
class User{
  @IsNotEmpty({message: '手机号不能为空'})
  @MinLength(5, { message: '手机号必须至少有5个字符'})
  @MaxLength(11, { message: '手机号不能超过11个字符'})
  phone: string
  password: string

  @IsNotEmpty({message: '年龄不能为空'})
  @Min(0,{message: '年龄最小值为0'})
  @Max(100,{message: '年龄最大值为100'})
  age: number
}
const user = new User()
user.phone = '15282088586888'
validate(user).then(errors =>{
  console.log(errors)
})

class-transformer 库

ts 复制代码
import { plainToClass, Type } from "class-transformer"
import axios from "axios"

class User {
    id: number
    firstName: string
    lastName: string
    
    @Type(() => Number)
    age: number

    getName() {
        return this.firstName + " " + this.lastName;
    }

    isAdult() {
        return this.age > 36 && this.age < 60;
    }
}

axios.get("https://api.myjson.com/bins/1b59tw").then(resp => resp.data)
    .then(users => {
        const us = plainToClass(User, users);
        for (const u of us) {
            console.log(typeof u.age, u.age);
        }
    })
相关推荐
昨晚我输给了一辆AE861 小时前
为什么现在不推荐使用 React.FC 了?
前端·react.js·typescript
Wect8 小时前
LeetCode 130. 被围绕的区域:两种解法详解(BFS/DFS)
前端·算法·typescript
Dilettante2588 小时前
这一招让 Node 后端服务启动速度提升 75%!
typescript·node.js
jonjia1 天前
模块、脚本与声明文件
typescript
jonjia1 天前
配置 TypeScript
typescript
jonjia1 天前
TypeScript 工具函数开发
typescript
jonjia1 天前
注解与断言
typescript
jonjia1 天前
IDE 超能力
typescript
jonjia1 天前
对象类型
typescript
jonjia1 天前
快速搭建 TypeScript 开发环境
typescript