装饰器
概述
面向对象的概念(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()
运行结果:
可以有多个装饰器修饰
成员装饰器
属性:
属性装饰器也是一个函数,该函数需要两个参数:
- 如果是静态属性,则为类本身;如果是实例属性,则为类的原型;
- 固定为一个字符串,表示属性名
ts
function d(target: any, key: string){
console.log(target, key)
}
class A {
@d
static prop1: string
@d
prop2: number
}
方法:
方法装饰器也是一个函数,该函数需要三个参数:
- 如果是静态方法,则为类本身;如果是实例方法,则为类的原型;
- 固定为一个字符串,表示方法名
- 属性描述对象
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);
}
})