• TS(五):装饰器


    启用装饰器支持

    修改 tsconfig.json

    {
        "experimentalDecorators": true, // 启用装饰器的实验性支持
        "emitDecoratorMetadata": true,  // 为源文件中的修饰声明发出设计类型元数据
    }
    
    • 1
    • 2
    • 3
    • 4

    类装饰器

    定义并使用装饰器

    const Decorator: ClassDecorator = (target: Function) => {
        console.log(target);
    
        target.prototype.sayName = () => {
            console.log((<any>target).firstName, (<any>target).lastName);
        }
    
    }
    
    @Decorator // [Function: Person] { firstName: 'Prosper', lastName: 'Lee' }
    class Person {
        public static firstName: string = "Prosper";
        public static lastName: string = "Lee";
    }
    
    const person: Person = new Person();
    (<any>person).sayName(); // Prosper Lee
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    对比不使用装饰器

    class Animal {
        public static firstName: string = "Dog";
        public static lastName: string = "Small";
    }
    Decorator(Animal); // [Function: Animal] { firstName: 'Dog', lastName: 'Small' }
    
    const animal = new Animal();
    (<any>animal).sayName(); // Dog Small
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    装饰器叠加

    const Decorator1: ClassDecorator = (target: Function) => {
        console.log('装饰器1', target);
    
        target.prototype.sayName = () => {
            console.log((<any>target).firstName, (<any>target).lastName);
        }
    
    }
    
    const Decorator2: ClassDecorator = (target: Function) => {
        console.log('装饰器2', target);
    
        target.prototype.sayHello = () => {
            console.log('Hello', (<any>target).firstName, (<any>target).lastName);
        }
    
    }
    
    @Decorator1
    @Decorator2
    class Person {
        public static firstName: string = "Prosper";
        public static lastName: string = "Lee";
    }
    
    /**
     * 运行结果
     *      先 -> 装饰器2 [Function: Person] { firstName: 'Prosper', lastName: 'Lee' }
     *      后 -> 装饰器1 [Function: Person] { firstName: 'Prosper', lastName: 'Lee' }
     */
    
    const person: Person = new Person();
    (<any>person).sayName(); // Prosper Lee
    (<any>person).sayHello(); // Hello Prosper Lee
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34

    实现消息提示统一响应

    enum MessageType {
        log = 'log',
        info = 'info',
        warn = 'warn',
        error = 'error',
    }
    
    interface MessageData {
        type: MessageType;
        message: string;
    }
    
    const MessageDecorator: ClassDecorator = (target: Function) => {
        console.log(target);
        target.prototype.$message = (data: MessageData) => {
            console[data.type](data.message);
        }
    }
    
    @MessageDecorator
    class Person {
        public sayMessage() {
            (<any>this).$message({ type: MessageType.log, message: 'Log Log Log !!!' });
            (<any>this).$message({ type: MessageType.info, message: 'Info Info Info !!!' });
            (<any>this).$message({ type: MessageType.warn, message: 'Warn Warn Warn !!!' });
            (<any>this).$message({ type: MessageType.error, message: 'Error Error Error !!!' });
        }
    }
    
    const person: Person = new Person();
    (<any>person).sayMessage();
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31

    装饰器工厂

    enum MessageType {
        log = 'log',
        info = 'info',
        warn = 'warn',
        error = 'error',
    }
    
    const MessageDecorator = (type: MessageType): ClassDecorator => {
        return (target: Function) => {
            console.log(target);
            target.prototype.$message = (message: string) => {
                console[type](message);
            }
        }
    }
    
    @MessageDecorator(MessageType.log)
    class Person {
        public sayMessage() {
            (<any>this).$message('Log Log Log !!!');
        }
    }
    
    const person: Person = new Person();
    (<any>person).sayMessage(); // Log Log Log !!!
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    方法装饰器

    const FuncDecorator: MethodDecorator = (target: Object, propertyKey: string | symbol, descriptor: PropertyDescriptor) => {
        console.log('静态成员的类的构造函数 / 实例成员的类的原型', target);
        console.log('成员的名称', propertyKey);
        console.log('成员的属性描述符', descriptor);
    
        const method = descriptor.value;
    
        // 通过装饰器修改原有方法
        descriptor.value = (...args: any[]) => {
            console.log(`修改了方法: ${propertyKey.toString()}`, args);
            method.apply(target, args);
        }
    }
    
    
    class FuncClass {
    
        /**
         * @FuncDecorator
         * 静态成员的类的构造函数 / 实例成员的类的原型       ƒ FuncClass() { }
         * 成员的名称                                   funcA
         * 成员的属性描述符                              {writable: true, enumerable: true, configurable: true, value: ƒ}
         */
        @FuncDecorator
        public static funcA(a1: string, a2: number) {
            console.log(a1, a2);
        }
    
        /**
         * @FuncDecorator
         * 静态成员的类的构造函数 / 实例成员的类的原型       {funcB: ƒ, constructor: ƒ}
         * 成员的名称                                   funcB
         * 成员的属性描述符                              {writable: true, enumerable: true, configurable: true, value: ƒ}
         */
        @FuncDecorator
        public funcB(b1: boolean) {
            console.log(b1);
        }
    
    }
    
    /**
     * 结果:
     *      修改了方法: funcA ['Lee', 20, 1, 2, 3]
     *      Lee 20
     */
    FuncClass.funcA('Lee', 20, 1, 2, 3);
    
    /**
     * 结果:
     *      修改了方法: funcB [true, 1, 2, 3]
     *      true
     */
    const func = new FuncClass();
    func.funcB(true, 1, 2, 3);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55

    登录状态验证

    在这里插入图片描述

    const ValidateTokenDecorator: MethodDecorator = (target: Object, propertyKey: string | symbol, descriptor: PropertyDescriptor) => {
        const method = descriptor.value;
        descriptor.value = (...args: any[]) => {
            // 登录验证相关代码
            if (localStorage.getItem('token')) {
                alert('您已登录,无需重复登录,正在跳转...');
            } else {
                method.apply(target, args);
            }
        }
    }
    
    class LoginController {
        @ValidateTokenDecorator
        public login(username: string, password: string) {
            localStorage.setItem('token', `token-${username}-${password}`);
            alert(`登录成功!\n用户名:${username}\n密码:${password}`);
        }
        public logout() {
            localStorage.clear();
            alert('退出成功!');
        }
    }
    
    const loginController = new LoginController();
    
    const loginBtn = document.createElement('button');
    loginBtn.innerText = "登录";
    loginBtn.onclick = () => loginController.login('Lee', '123456');
    document.body.append(loginBtn);
    
    const logoutBtn = document.createElement('button');
    logoutBtn.innerText = "退出";
    logoutBtn.onclick = () => loginController.logout();
    document.body.append(logoutBtn);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35

    数据请求

    在这里插入图片描述

    服务端

    const http = require('http');
    const url = require('url');
    
    http.createServer((req, res) => {
        res.writeHead(200, { "Access-Control-Allow-Origin": "*" });
    
        if (req.method === 'GET') {
            const { query } = url.parse(req.url, true);
            const result = { code: 200, msg: 'success', data: query };
            res.end(JSON.stringify(result));
        } else {
            const result = { code: 500, msg: 'fail', data: null };
            res.end(JSON.stringify(result));
        }
    
    }).listen(8888);
    
    console.log('Server running at http://127.0.0.1:8888/');
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    客户端

    interface RequestParams {
        [prop: string]: any
    }
    
    interface RequestResponse {
        code: number;
        msg: string;
        data: any;
    }
    
    enum RequestMethod {
        GET = "GET",
        POST = "POST",
        DELETE = "DELETE",
        PUT = "PUT",
    }
    
    const RequestDecorator = (method: RequestMethod, url: string): MethodDecorator => {
        return (target: Object, propertyKey: string | symbol, descriptor: PropertyDescriptor) => {
            const original = descriptor.value;
            descriptor.value = (params: RequestParams): Promise<RequestResponse | RequestParams> => {
                return new Promise((resolve: (value: any) => void, reject: (reason?: any) => void) => {
                    url += `?`;
                    for (const key in params) { url += `${key}=${params[key]}&`; }
                    const xhr: XMLHttpRequest = new XMLHttpRequest();
                    xhr.open(method, url);
                    xhr.send();
                    xhr.onreadystatechange = () => {
                        if (xhr.readyState === 4) {
                            if (xhr.status === 200) {
                                const result: RequestResponse = JSON.parse(xhr.response);
                                result.code === 200 ? resolve(result) : reject(result);
                            } else {
                                original(params);
                            }
                        }
                    }
                })
            }
        }
    }
    
    class RequestController {
    
        @RequestDecorator(RequestMethod.GET, "http://127.0.0.1:8888/")
        public request_01(params: RequestParams): Promise<RequestResponse | RequestParams> { return Promise.reject(params); }
    
        @RequestDecorator(RequestMethod.POST, "http://127.0.0.1:8888/")
        public request_02(params: RequestParams): Promise<RequestResponse | RequestParams> { return Promise.reject(params); }
    
        @RequestDecorator(RequestMethod.POST, "http://127.0.0.1:1000/")
        public request_03(params: RequestParams): Promise<RequestResponse | RequestParams> { return Promise.reject(params); }
    
    }
    
    const requestController = new RequestController();
    
    const requestBtn01 = document.createElement('button');
    requestBtn01.innerText = "请求 01";
    requestBtn01.onclick = async () => {
        const res = await requestController.request_01({ username: 'Lee', password: '123456' });
        // {"code":200,"msg":"success","data":{"username":"Lee","password":"123456"}}
        console.log(res); 
    };
    
    const requestBtn02 = document.createElement('button');
    requestBtn02.innerText = "请求 02";
    requestBtn02.onclick = async () => {
        const res = await requestController.request_02({ username: 'Lee', password: '123456' });
        // Uncaught (in promise) {code: 500, msg: 'fail', data: null}
        console.log(res);
    };
    
    const requestBtn03 = document.createElement('button');
    requestBtn03.innerText = "请求 03";
    requestBtn03.onclick = async () => {
        const res = await requestController.request_03({ username: 'Lee', password: '123456' });
        // POST http://127.0.0.1:1000/?username=Lee&password=123456& net::ERR_CONNECTION_REFUSED
        // Uncaught (in promise) {username: 'Lee', password: '123456'}
        console.log(res);
    };
    
    document.body.append(requestBtn01);
    document.body.append(document.createElement('hr'));
    document.body.append(requestBtn02);
    document.body.append(document.createElement('hr'));
    document.body.append(requestBtn03);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87

    属性装饰器

    大小写转换

    const PropDecorator: PropertyDecorator = (target: Object, propertyKey: string | symbol) => {
        console.log('静态成员的类的构造函数 / 实例成员的类的原型', target);
        console.log('成员的名称', propertyKey);
    
        // 静态属性转大写
        if (typeof target === 'function') {
            (<any>target)[propertyKey] = (<any>target)[propertyKey].toUpperCase();
        } else { // 一般属性转小写
            let value: string;
            Object.defineProperty(target, propertyKey, {
                get: () => value.toLowerCase(),
                set: (v: any) => value = v,
            });
        }
    }
    
    class Person {
    
        /**
         * @PropDecorator
         *      静态成员的类的构造函数 / 实例成员的类的原型          {constructor: ƒ}
         *      成员的名称                                      firstName
         */
        @PropDecorator
        public firstName: string = "Prosper";
        
        /**
         * @PropDecorator
         *      静态成员的类的构造函数 / 实例成员的类的原型          [class Person] { lastName: 'Lee' }
         *      成员的名称                                      lastName
         */
        @PropDecorator
        public static lastName: string = "Lee";
    
    }
    
    const person: Person = new Person();
    console.log(`${person.firstName}${Person.lastName}!!!`); // prosperLEE!!!
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38

    元数据

    安装依赖

    $ npm i reflect-metadata
    
    • 1

    基础用法

    import 'reflect-metadata';
    
    let person = { name: 'Lee' };
    
    // 描述name属性的基础信息
    Reflect.defineMetadata('info', { key: 'string', value: 'string', desc: '这是一个名字字段!' }, person, 'name');
    
    // 打印name字段的基础信息
    const info = Reflect.getMetadata('info', person, 'name');
    console.log(info); // { key: 'string', value: 'string', desc: '这是一个名字字段!' }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    参数装饰器

    验证参数是否为必填项

    import 'reflect-metadata';
    
    interface ErrorParam {
        parameterIndex: number;
        message: string;
    }
    
    enum Sex {
        Empty = '',
        Sir = '先生',
        Madam = '女士',
    }
    
    /**
     * 传参验证方法装饰器
     * @param target 静态成员的类的构造函数 / 实例成员的类的原型
     * @param propertyKey 成员的名称
     * @param descriptor 成员的属性描述符
     */
    const ValidateDecorator: MethodDecorator = (target: Object, propertyKey: string | symbol, descriptor: PropertyDescriptor) => {
        const original = descriptor.value;
        descriptor.value = (...args: any) => {
            const rules: ErrorParam[] = Reflect.getMetadata('rules', target, propertyKey) || [];
            rules.forEach((rule: ErrorParam) => {
                if (args[rule.parameterIndex] === undefined) {
                    throw new Error(rule.message);
                }
            })
            original.apply(target, args);
        }
    }
    
    /**
     * 参数装饰器工厂
     * @param field 字段名
     * @returns 参数装饰器
     */
    const RequiredDecorator = (field: string): ParameterDecorator => {
        return (target: Object, propertyKey: string | symbol | undefined, parameterIndex: number) => {
            if (propertyKey) {
                let rules: ErrorParam[] = [
                    { parameterIndex, message: `缺少参数: '${field}'` },
                    ...(Reflect.getMetadata('rules', target, propertyKey) || []),
                ];
                Reflect.defineMetadata('rules', rules, target, propertyKey);
            }
        }
    }
    
    class Person {
    
        @ValidateDecorator
        public message(
            @RequiredDecorator('敬语')
            honorific: string,
            @RequiredDecorator('姓名')
            name: string,
            sex: Sex
        ) {
            console.log(`${honorific} ${name} ${sex ? sex : Sex.Empty}!!!`);
        }
    
    }
    
    const person = new Person();
    
    person.message();                          // Error: 缺少参数: '敬语'
    person.message('尊敬的');                   // Error: 缺少参数: '姓名'
    person.message('尊敬的', 'Lee');            // 尊敬的 Lee !!!
    person.message('尊敬的', 'Lee', Sex.Sir);   // 尊敬的 Lee 先生!!!
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
  • 相关阅读:
    堆的介绍与堆的实现和调整
    【Python】三、内置函数
    Linux系统编程学习 NO.8 ——make和Makefile、进度条程序
    如何看待是大数据技术?
    【基于Arduino的垃圾分类装置开发教程一项目书】
    s19.基于 Kubernetes v1.25.0(kubeadm) 和 Docker 部署高可用集群(一)
    Haskell添加HTTP爬虫ip编写的爬虫程序
    vue3 + ts项目(无vite)报错记录
    国际物流和跨境电商物流的区别
    “灯塔工厂”的中国路径:智造从点到面铺开
  • 原文地址:https://blog.csdn.net/weixin_43526371/article/details/133808613