好文推荐
localStorage的别样用法
借助npm包统一包管理器
前言
约是两年前,就对着ts文档学习了装饰器的使用,我是相信温故而知新的,故今天重新在学一遍,相信会有不一样的收获
启用
在项目中,需要在tsconfig.json中显示的启用
{
"compilerOptions": {
"experimentalDecorators": true
}
}
装饰器分类
1-类装饰器
2-属性装饰器
3-方法装饰器
4-参数装饰器
5-访问器装饰器
执行顺序
- 参数装饰器,然后依次是方法装饰器,访问符装饰器,或属性装饰器应用到每个实例成员
- 参数装饰器,然后依次是方法装饰器,访问符装饰器,或属性装饰器应用到每个静态成员
- 参数装饰器应用到构造函数。
- 类装饰器应用到类。
使用
- 类装饰器
1-其ts定义如下
可以看到,类的本质是Function类型,这意味着,我们可以通过prototype来进行扩展,同时从返回值可以猜测,如果返回值类型不是void,则极大可能性会将返回的值作为装饰器本身
declare type ClassDecorator = <TFunction extends Function>(target: TFunction) => TFunction | void;
2-返回void
function sex<T extends {new(...args:any[]):{}}>(constructor:T){
constructor.prototype.logSex = function(){
console.log('男')
}
}
@sex
class Person{
}
(new Person() as any).logSex()
3-返回Function
上一个返回void的示例是无法传递参数的,可以通过返回函数类型来解决
function sex(msg:string){
return function<T extends {new(...args:any[]):{}}>(constructor:T){
constructor.prototype.logSex = function(){
console.log(msg)
}
}
}
@sex('用户的')
class Person{
}
(new Person() as any).logSex()
4-重载
由于class本质上也是Function类型,所以这可以看作是3的另一种表达形式,且更灵活些
function sex<T extends {new(...args:any[]):{}}>(Tar:T){
return class extends Tar{
constructor(...args:any[]){
super()
}
}
}
@sex
class Person{
}
- 属性装饰器
1-定义
从定义可知,其也是一个函数,且被执行时会接收两个参数
1-对于静态成员来说是类的构造函数,对于实例成员是类的原型对象
2-成员的名字
declare type PropertyDecorator = (target: Object, propertyKey: string | symbol) => void;
2-使用
我们可以对某个属性进行监控,并当某个操作行为发生时执行一些关联动作
function reactive(Tar:any,p:string){
let value = Tar[p]
const get = function(){
return value
}
const set = function(newValue:any){
Tar.changeName(newValue)
value = newValue
}
if(Reflect.deleteProperty(Tar,p)){
Object.defineProperty(Tar,p,{
set,
get,
enumerable:true,
configurable:true
})
}
}
class Person{
@reactive
name:string=''
changeName(newName:string){
console.log('改名字了,新名字是:'+newName)
}
}
const p = new Person()
p.name
p.name = '23'
console.log(p.name)
- 方法装饰器
1-定义
declare type MethodDecorator =
<T>(target: Object, propertyKey: string | symbol, descriptor: TypedPropertyDescriptor<T>)
=> TypedPropertyDescriptor<T> | void;
相比较属性选择器,其多了参数三,其对应的ts定义如下
value属性表示的是函数体,这意味着可以先将原函数体缓存后执行重写等操作
interface TypedPropertyDescriptor<T> {
enumerable?: boolean;
configurable?: boolean;
writable?: boolean;
value?: T;
get?: () => T;
set?: (value: T) => void;
}
2-示例
拿我之前写npm包时遇到的一个需求举例,在初始化的时候用到了异步数据,在异步执行结束前如果用户执行了do方法,则需要对其进行缓存,等异步结束后再取回依次调用,伪代码如下
import { config } from '.'
function delay(_,_, descriptor){
const value = descriptor.value
descriptor.value = function(...args:any[]){
if(config.asyning){
config.que.push(descriptor)
}else{
value.apply(this,args)
config.que = []
}
}
return descriptor
}
class NAS{
@delay
do(){
}
}
- 参数装饰器
1-定义
declare type ParameterDecorator =
(target: Object, propertyKey: string | symbol, parameterIndex: number) => void;
2-示例
在参数装饰器执行时收集必选信息,当运行方法装饰器时重写descriptor.value并加入判断逻辑
const requiredMap:any = {
}
function required(_: any, propertyKey: string, parameterIndex: number){
requiredMap[propertyKey]=parameterIndex
}
function validate(_:any,__:any, descriptor:any){
const i = requiredMap[descriptor.value.name]
if(i !== undefined){
descriptor.value = (...args:any[])=>{
if(args[i] === undefined){
throw new Error('参数缺失')
}
}
}
return descriptor
}
class Person{
@validate
changeName(@required newName:string){
console.log('改名字了,新名字是:'+newName)
}
}
const p = new Person()
p.changeName()
- 访问器装饰器
网友评论