浅谈js装饰器

495 阅读3分钟

介绍

js装饰器目前(2023/3/24)还处于提案Stage-3阶段,一共有 5 个状态,分别是: Stage 0: Strawman(展示阶段) Stage 1: Proposal(征求意见阶段) Stage 2: Draft(草案阶段) Stage 3: Candidate(候选阶段) Stage 4: Finished(定案阶段)。现在使用装饰器的语法,需要借助babel来编译。 学过java的,看到js的@xxx装饰器语法,很容易想到java的注解。虽然实现的功能有些类似,但内部的实现机制不同。效果上都是在不改变原有类、属性、方法的情况下,给它们增加额外的功能,并且同一个装饰器可以增强多个类、属性、方法,达到代码的最大复用。

环境配置

vscode里编辑@xxx默认会报错 image.png 需要在设置里开启装饰器的支持

image.png 编译报错,安装装饰器支持插件:

yarn add @babel/plugin-proposal-class-properties @babel/plugin-proposal-decorators

plugin-proposal-decorators支持装饰器语法的插件,plugin-proposal-class-properties编译class的插件 package.json文件中配置:

"babel": {
    "presets": [
      "react-app"
    ],
    "plugins": [
      [
        "@babel/plugin-proposal-decorators",
        {
          "legacy": true
        }
      ],
      [
        "@babel/plugin-proposal-class-properties",
        {
          "loose": true
        }
      ]
    ]
  }

使用案例:

下面可以开始使用装饰器语法写代码了。

类装饰器

const test = (target) => {
    target.sName = 'Tom'
    target.getAge = function () {
        return 20;
    }
}

@test
class Student{

}

console.dir(Student);

image.png

image.png 通过浏览器调试工具的Sources里可以看到语法糖编译后的代码:

var _class;
let Student = test(_class = class Student {}) || _class;
console.dir(Student);

将Student类做为参数传入test函数中,target就是Student类,在类上挂载属性和方法 如果装饰器test有返回值,则使用返回值替代Student类,没有返回值则为Student类。

方法装饰器

function log(target, name, descriptor) {
    // 保存原始方法
    const original = descriptor.value;
  
    // 修改方法的行为
    descriptor.value = function (...args) {
      console.log(`Before calling ${name} with arguments: ${args}`);
      const result = original.apply(this, args);
      console.log(`After calling ${name}: ${result}`);
      return result;
    };
  
    // 返回修改后的方法
    return descriptor;
  }
  
  // 使用装饰器修改方法的行为
  class Student {
    @log
    myMethod(arg1, arg2) {
      return arg1 + arg2;
    }
  }
  
  // 创建 Student 的实例
  const myObject = new Student();
  
  // 调用 Student 的方法
  myObject.myMethod(1, 2);

image.png 方法装饰器可以在不改变原有方法的情况下,在方法执行前后插入业务代码,如打印日志、数据校验等。运用了代理模式,可以达到AOP切面编程效果。

属性装饰器

function readonly(target, key, descriptor) {
  descriptor.writable = false;
  return descriptor;
}

class Person {
  @readonly
  name = '张三';
}

const p = new Person();
p.name = '李四'; // 抛出 TypeError

image.png 属性装饰器可以修改属性是否只读,是否可以迭代,是否可删除。

js的装饰器和java注解的区别

JavaScript装饰器是一种函数,它可以修改类、方法或属性的行为。Java注解是一种元数据,它可以用于提供类、方法或属性的信息。 JavaScript 装饰器是在运行时执行的,它们可以动态地修改类、方法或属性的行为。Java注解是通过反射机制实现的,它们可以在运行时被读取和处理,但不会动态改变原有的类、方法或属性。