JavaScript、TypeScript 装饰器

需要掌握:

作用

在 不改动源代码 的情况下,增加 额外 功能

应用范围

可以用在 类,属性,类中的方法 或 方法参数 上

注意:不能修饰函数

理解

与 python的装饰器 很相似

其功能与 java的注解 类似,但运行原理不同

应用场景

  • 打印日志
  • 性能测试
  • 权限校验等
  • 数据库事务处理

实现原理

本质上是闭包

支持情况

浏览器、Node.js

在当前的 浏览器 或 Node.js 版本中,尚不支持装饰器

因此我们使用使用 Babel 插件 @babel/plugin-proposal-decorators 来将它转换为 浏览器 或 Node.js 中可识别的代码。

typescript

支持装饰器

方法装饰器

普通代码

下面例子中,创建 User 类,里面有 login() 登录方法。

在开发时,经常会测试方法的 执行效率,看是否符合公司要求,在该方法中,两次获取时间,进行相减,计算出登录的 耗时 操作

class User {

  login(username:string,password:string):Object{
    const start = Date.now()
    console.log("用户名:",username)
    console.log("密码:",password)
    console.log("模拟处理登录操作")
    const end = Date.now()

    console.log("耗时:",(end-start),"毫秒")
    return {
      code:0,
      message:'登录成功!'
    }
  }

} 

const user = new User()
const res = user.login("lilei","123456")

缺点

  • 由于 测试耗时的代码,与 登录业务的代码不相关,从规范上讲,不应该混到一起

  • 测试耗时的代码,当符合要求后,是要删除的,改动代码后,还要再次进行测试,因为防止 无意中改动关键代码 导致 报错,这样就增加工作量

解决

使用 装饰器,将 测试耗时的代码 放到 装饰器中

定义装饰器

function test_time(target: any, context: any) {

  /*
   注意:此处第一个参数必须是 this
   否则 login() 方法中 this.age 将获取不到 值
   */
  return function (this:any , ...args: any[] ){
      console.log(`开始执行:${context.name}方法`)
      const start = Date.now()
      // 通过 call() 方法改变 login() 方法中的this
      const res = target.call(this , ...args)
      const end = Date.now()
      console.log(`结束执行:${context.name}方法,耗时${end-start}毫秒`)
      return res
  }
}
解释
  • target:此处表示修饰的方法

  • context:上下文,有以下属性。

    • kind:值固定为字符串method,表示当前为方法装饰器。
    • name:所装饰的方法名,类型为字符串或 Symbol 值。
    • static:布尔值,表示是否为静态方法。该属性为只读属性。
    • private:布尔值,表示是否为私有方法。该属性为只读属性。
    • access:对象,包含了方法的存取器,但是只有get()方法用来取值,没有set()方法进行赋值。
    • addInitializer():为方法增加初始化函数。

使用装饰器

class User {
  age:number = 20

  @test_time
  login(username:string,password:string):Object{
    console.log("age",this.age)
    console.log("用户名:",username)
    console.log("密码:",password)
    return {
      code:0,
      message:'登录成功!'
    }
  }
}

执行结果:

始执行:login方法
age 20
用户名: lilei
密码: 123456
结束执行:login方法,耗时1毫秒
结果: { code: 0, message: '登录成功!' }

本质

方法装饰器会改写类的原始方法,实质等同于替代所装饰的函数

class User {
  age:number = 20

  login(username:string,password:string):Object{
      console.log(`开始执行:login方法`)
      const start = Date.now()


      console.log("age",this.age)
      console.log("用户名:",username)
      console.log("密码:",password)

      const end = Date.now()
      console.log(`结束执行:login方法,耗时${end-start}毫秒`)

      return {
          code:0,
          message:'登录成功!'
      }
  }
}

参考:
https://zhuanlan.zhihu.com/p/189960001
https://typescript.p6p.net/typescript-tutorial/decorator.html


原文出处:/show_1GW5aZMFoTv.html