你真的会装饰器了吗?

最近学习node的服务端框架nest, 发现装饰器语法无处不在,比如controller中编写接口路径

使用各种AOP切面

其实不止Nest, Vue,MobX等也是广泛的使用装饰器语法,所以学会装饰器势在必行。

很久以前,看过es6中关于装饰器的讲解,学了不用就会很快忘记,趁着这次,再复习一遍装饰器的概念和用法。

装饰器是什么?

装饰器是一个普通的函数,使用@标记。顾名思义,就是对目标起到装饰作用,可以在不修改原始代码的情况下为其添加新的行为。只能用于装饰类和类的属性以及类的方法。简而言之,装饰器就是用来扩展类和类的属性功能的。

类是作为一个参数传入到装饰器中

js 复制代码
@decorator
class A {}

// 等同于

class A {}
A = decorator(A) || A;

注意:** 装饰的过程在代码编译时发生**

用法

装饰器一共三种用法:

  • 装饰类
  • 装饰类的属性
  • 装饰类的方法

装饰类

可以给类添加静态属性,静态方法,通过修改原型添加类的实例属性和实例方法。类作为装饰器的唯一参数

定义一个空类A, 通过装饰器添加其属性和方法

js 复制代码
 function decorator(target) {
    // target: 被装饰的类 class A {}
    target.test = '123' // 添加静态属性
    target.testfunc = () => {
        return 'testfunc'
    } // 添加静态方法
    target.prototype.testp = '456' // 添加实例属性
    target.prototype.testpf = () => {
        return 'testpf'
    } // 添加实例方法
        
}

@decorator
class A {}
const a = new A()
console.log(a.testp) // '456'
console.log(a.testpf()) // 'testpf'
console.log(A.test) // '123'
console.log(A.testfunc()) // 'testfunc'

如果需要从装饰器函数调用时传递参数,可以在装饰器函数中返回一个函数:

js 复制代码
function decorator(str) {
    return (target) => {
        target.prototype.test = str
    }
}

@decorator('abc')
class A {}
const a = new A()
console.log(a.test) // 'abc'

装饰类的属性

装饰器函数可以接收三个参数:

  • 第一个参数: 被装饰的目标所在的类
  • 第二个参数: 被装饰的目标名
  • 第三个参数: 被装饰的目标的属性描述符

在类中我们经常会看到一个属性只读,如下:

ts 复制代码
function readonly(target, name, descriptor) {
    // target: 被装饰的类 class B {}
    // name: 被装饰的目标名 ele
    // descriptor: 被装饰的目标的属性描述符
    descriptor.writable = false 
}
class B {
    @readonly ele = '123' // 会报错
}

装饰类的方法

接收的参数和装饰属性一致

在这里,我们定义一个类C, C中有一个方法,需要在方法返回的结果值末尾加上一些字符串:

ts 复制代码
function concatStr(arg) {
    return (target, name, descriptor) => {
        const old = descriptor.value
        descriptor.value = function (...args) {
            return old.apply(this, [...args]) + arg
        }
    }
}
class C {
    @concatStr('how are you?')
    sum(a, b) {
        return a + b
    }
}
const c = new C()
console.log(c.sum('hello', 'i am xiaoming ,')) // 'hello i am xiaoming, how are you?'

注意: 装饰器不能装饰普通的函数,因为普通函数存在变量声明提升。

应用

  • 节流防抖函数

    有了装饰器,我们只需要在函数前通过@标记一个装饰器函数就可以了,通俗易懂。 这里实现防抖函数,节流函数类似。

    debounce.js

js 复制代码
  function debounce(delay) {
    let timer = null;
    return function (target, name, descriptor) {
        const originFn = descriptor.value;
        descriptor.value = function (...args) {
            if (timer) {
                clearTimeout(timer);
            }
            timer = setTimeout(() => {
                originFn.apply(this, args);
            }, delay);
        }
        return descriptor;
    }
 class TestDebounce {
    @debounce(2000)
    handleMouseMove(val) {
        console.log('handleMouseMove', val)
    }
 }

页面上监听mousemove事件

js 复制代码
 const p = new TestDebounce();
 window.addEventListener('mousemove', p.handleMouseMove.bind(null, 'hahaha'));

Over

相关推荐
Q_Q5110082853 小时前
python的保险业务管理与数据分析系统
开发语言·spring boot·python·django·flask·node.js·php
摘星小杨15 小时前
安装nvm管理node.js,详细安装使用教程和详细命令
node.js·nvm
灋✘逞_兇17 小时前
Node.Js是什么?
服务器·javascript·node.js
归于尽1 天前
回调函数在Node.js中是怎么执行的?
前端·javascript·node.js
GDAL1 天前
多字节字符的字节被拆分到不同 chunk 中,导致解码失败
node.js
Jacob02341 天前
“Node.js 不行了”?性能争议中的误解与选择真相
后端·node.js
全宝1 天前
前端也能这么丝滑!Node + Vue3 实现 SSE 流式文本输出全流程
前端·javascript·node.js
天天进步20152 天前
前端工程化:Webpack从入门到精通
前端·webpack·node.js
实习生小黄2 天前
express 连接在线数据库踩坑
node.js·express
伍哥的传说2 天前
H3初识——入门介绍之常用中间件
前端·javascript·react.js·中间件·前端框架·node.js·ecmascript