装饰器语法

简介: @decorator 装饰器是 es7 更新的提案,是一种与类相关的语法,用来注释或修改类和类的方法,是在装饰器模式的基础上产生的。装饰器是过去几年中js最大的成就之一,已是ES7的标准特性之一。

前言


@decorator 装饰器是 es7 更新的提案,是一种与类相关的语法,用来注释或修改类和类的方法,是在装饰器模式的基础上产生的。装饰器是过去几年中js最大的成就之一,已是ES7的标准特性之一。

装饰器是一种特殊类型的声明,它能够被附加到类声明,方法,属性或参数上,可以修改类的行为。 通俗的讲装饰器就是一个方法,可以注入到类、方法、属性参数上来扩展类、属性、方法、参数的功能。

常见的装饰器有:类装饰器、属性装饰器、方法装饰器、参数装饰器

装饰器的写法:普通装饰器(无法传参) 、 装饰器工厂(可传参,下面的内容都将基于工厂模式)

先来了解一下普通装饰器的写法,工厂模式的写法在后面会看到很多,就不在这里介绍了

function Decorator(targetClass: any) {
  console.log(targetClass);
  targetClass.prototype.msg = "装饰器注入的实例属性";
  targetClass.func = function () {
    console.log("装饰器注入的方法");
  };
}
@Decorator
class Animals {
  constructor() {}
}
const animals: any = new Animals();
// @ts-ignore
Animals.func();
console.log(animals.msg);
复制代码

可以直接再类上添加静态属性和静态方法,也可以通过prototype来添加实例方法

运行结果如下(如果使用TS来写代码,Node环境下需要配置typescript环境,这里我直接使用deno运行)

1682521761(1).png


类装饰器


上面的普通装饰器的示例就是一个类装饰器,这里我们将它重构一下,使用工厂模式来实现

function Decorator(params: any) {
  return function (targetClass: any) {
    targetClass.prototype.msg = `来自装饰器注入的消息${params}`;
  };
}
@Decorator("args")
class Animals {
  constructor() {}
}
const animals: any = new Animals();
console.log(animals.msg);
复制代码

实现方式类似于柯里化

1682521784(1).png

使用装饰器的时候传递参数,在使用一些Node框架的时候经常遇到,类似于@Controller('/users'),通过装饰器指定controller的路由,这种情境下使用普通的装饰器函数已经无法满足,而工厂模式则可以轻而易举的完成这个任务。

类装饰器表达式会在运行时当作函数被调用,类的构造函数作为其唯一的参数; 如果类装饰器返回一个值,它会使用提供的构造函数来替换类的声明;所以装饰器除了注入新的属性,也可以用来重载类的属性和方法,但是必须重载所有的属性和方法


属性装饰器


顾名思义,属性装饰器用来修饰类的属性,可以用在类的属性、方法、get/set 函数中,属性装饰器接收三个参数,目标类、被装饰的属性key、被装饰的属性描述,例如我们可以通过装饰器来将属性变为只读

function readonly() {
  return function (
    target: unknown,
    key: string,
    descriptor: PropertyDescriptor
  ) {
    console.log(target, key, descriptor);
    console.log(descriptor.value.toString());
    descriptor.writable = false;
    return descriptor;
  };
}
class Person {
  constructor() {}
  @readonly()
  sayHi() {
    console.log("Hi");
  }
}
const person = new Person();
person.sayHi();
person.sayHi = function () {};
复制代码

除了修改方法描述,还可以使用target[key]的形式来获取属性值或者修改属性值。(修饰方法时可以通过descriptor.value来获取函数体,后面会用到)

1682521807(1).png

通过只读修饰之后的属性再修改属性值的时候就会报错。属性装饰器装饰方法的时候可以用来实现日志操作以及方法的拦截

修饰方法的装饰器最后必须返回属性描述descriptor


参数装饰器


参数装饰器用来装饰方法中的形参,装饰参数时接收三个参数:目标类、方法名、参数在arguments中的索引

function LogParams(params: string) {
  return function (target: unknown, methodName: string, index: number) {
    console.log(target, methodName, index);
    console.log(`监听${params}`);
  };
}
class Person {
  constructor() {}
  sayHi(@LogParams("user") user: string) {
    console.log("Hi", user);
  }
}
const person = new Person();
person.sayHi("king");
复制代码

参数装饰器只能用来监视一个方法的参数是否被传入,并不能做太多的处理,所以并不常用

1682521841(1).png


装饰器的妙用


防抖&节流

节流防抖使我们日常开发中经常使用的性能优化的手段,之前的使用都需要封装一层函数,看起来也不舒服,现在有了装饰器,我们可以非常“爽”地进行防抖和节流的优化

// 节流
const throttle = (time: number) => {
  let prev = new Date().getTime();
  return (target: unknown, name: string, descriptor: PropertyDescriptor) => {
    // 前面的示例中说到过,通过descriptor.value获取函数体
    const func = descriptor.value;
    if (typeof func === "function") {
      descriptor.value = function (...args: any[]) {
        const now = new Date().getTime();
        if (now - prev > time) {
          func.apply(this, args);
          prev = new Date().getTime();
        }
      };
    }
  };
};
// 防抖
const debounce = (time: number) => {
  let timer: number;
  return (target: unknown, name: string, descriptor: PropertyDescriptor) => {
    const func = descriptor.value;
    if (typeof func === "function") {
      descriptor.value = function (...args: any[]) {
        if (timer) clearTimeout(timer);
        timer = setTimeout(() => {
          func.apply(this, args);
        }, time);
      };
    }
  };
};
复制代码

使用起来也非常的舒服,比如组件要监听滚动事件,我们就可以直接在绑定的函数上使用装饰器

class App extends React.Component {
    componentDidMount() {
        window.addEveneListener('scroll', this.scroll);
    }
    componentWillUnmount() {
        window.removeEveneListener('scroll', this.scroll);
    }
    @throttle(50)
    scroll() {}
}
复制代码

类型校验

类型校验主要应用于JavaScript,typescript自带类型检验,所以不太有必要使用

const validate = (type) => (target, name) => {
    if (typeof target[name] !== type) {
        throw new Error(`TypeError: attribute ${name} must be ${type} type`)
    }
}
class Form {
    @validate('string')
    static name = 111 // TypeError: attribute name must be ${type} type
}


相关文章
|
4月前
|
测试技术 Python
装饰器
【8月更文挑战第1天】
25 2
|
7月前
|
JavaScript
03_装饰器
03_装饰器
69 1
|
7月前
|
Python
【译】PEP-3129 类装饰器
【译】PEP-3129 类装饰器
49 3
|
7月前
|
自然语言处理 Java 编译器
【译】PEP 318--函数和方法的装饰器
【译】PEP 318--函数和方法的装饰器
48 0
|
7月前
|
数据安全/隐私保护 Python
解释装饰器(decorator)的功能和用法。
解释装饰器(decorator)的功能和用法。
58 1
|
存储 JavaScript 前端开发
装饰器语法
利用typeof判定类型的取值范围是:'undefined' /'boolean' /'string' /'number' /'object' /'function' /'symbol' 在JavaScript内部使用typeof判断类型依据的是二进制,根据变量的机器码低位1-3位存储其类型信息,有如下规则:
116 0
|
存储 缓存 关系型数据库
Python装饰器1-闭包与函数装饰器
闭包与函数装饰器:被装饰函数不带参数、被装饰函数带参数、装饰器带参数,装饰器的调用
Python装饰器1-闭包与函数装饰器
|
JSON 数据格式
装饰器的实际应用
使用装饰器模式改造slf4j打印json格式日志
770 0
装饰器的实际应用
|
缓存 测试技术 Python
【Python函数式编程】——装饰器
装饰器本质上是一个Python函数,它可以让其他函数在不需要做任何代码变动的前提下增加额外功能,装饰器的返回值 也是一个函数对象。 它经常用于有以下场景,比如:插入日志、性能测试、事务处理、缓存、权限校验等场景,装饰器是解决这类问题的绝佳设计。
156 0
【Python函数式编程】——装饰器
|
设计模式 程序员 Python
Python函数装饰器指南
Python 具有强大的功能和富有表现力的语法。我最喜欢的装饰之一。在设计模式的上下文中,装饰器动态更改方法或类功能,而不必直接使用子类。当您需要扩展功能,但不想修改原函数时,这是理想的选择。我们可以在任何地方实现装饰器模式,但是 Python 通过提供更具表现力的功能和语法来促进实现。
131 0