装饰器

装饰器是一种特殊的函数,在不更改原有类和类属性的代码的基础,扩展类的属性和方法。 装饰器作用非常类似设计模式中的装饰者模式。

# 类的装饰

在对类进行装饰的时候,装饰器接收一个参数,该参数就是被装饰的类。举一个简单的例子,定义一个Person类:

class Person {}

目前这个Person类中什么都没有。再定义一个装饰器函数,这个函数会赋予Person类一个gender静态属性。

function getgender (target) {
    target.gender = 'female'
}

然后使用装饰器对类进行装饰:

@getgender
class Person {}

这时候Person这个类就具有了gender属性。

Person.name // Jack

不难发现,装饰器这个名字虽然有些令人费解,但是作用还是非常清晰明了的。就是使用一个函数来对原有类进行增强,使用装饰器的好处主要有两点:

  1. 增强代码可读性。装饰器的名称可以自定义,相当于一个注释,解释了装饰器的主要作用。
  2. 不改变原有类代码。装饰器仅仅是在类的前面加入了一行代码来扩展类的功能,对类本身的代码不做任何更改。

# 类方法的装饰

除了对类整体进行装饰,装饰器还可以单独对类中的属性进行装饰。对属性进行装饰时,装饰器接收三个参数,以下面的readonly装饰器为例子:

function readonly(target, name, descriptor){
  descriptor.writable = false; // 将可写属性设为false
  return descriptor;
}

三个参数分别是:

  • target。属性所属的类的原型。
  • name。被装饰的属性的名称。
  • descriptor。装饰属性名的描述对象,用于定义属性的特特定行为。比如设置get和set,或者定义writable和configurable。

将该装饰器用于装饰类的某一个属性。

class Person {
  @readonly
  name() { return `${this.first} ${this.last}` }
}

一个属性可以拥有多个装饰器。此时装饰器会先从外到内进入,然后从内到外执行。

function dec(id){
    console.log('evaluated', id);
    return (target, property, descriptor) =>console.log('executed', id);
}

class Example {
    @dec(1)
    @dec(2)
    method(){}
}
// evaluated 1
// evaluated 2
// executed 2
// executed 1

更靠近属性的装饰器会先执行。

# 装饰器不可装饰函数

装饰器不能用于装饰函数,因为JavaScript中存在函数提示,会导致用于函数的装饰器发生意料之外的效果。例如

var counter = 0;

var add = function () {
  counter++;
};

@add
function foo() {
}

本意是执行完上面的代码后,counter等于1,但是实际运行代码发现counter还是0。因为函数声明提示的存在,导致最终执行的代码实际上是这样的:

var counter;
var add;

@add
function foo() {
}

counter = 0;

add = function () {
  counter++;
};
上次更新:: 2023/12/27 10:36:30