装饰器
装饰器是一种特殊的函数,在不更改原有类和类属性的代码的基础,扩展类的属性和方法。 装饰器作用非常类似设计模式中的装饰者模式。
# 类的装饰
在对类进行装饰的时候,装饰器接收一个参数,该参数就是被装饰的类。举一个简单的例子,定义一个Person类:
class Person {}
目前这个Person类中什么都没有。再定义一个装饰器函数,这个函数会赋予Person类一个gender静态属性。
function getgender (target) {
target.gender = 'female'
}
然后使用装饰器对类进行装饰:
@getgender
class Person {}
这时候Person这个类就具有了gender属性。
Person.name // Jack
不难发现,装饰器这个名字虽然有些令人费解,但是作用还是非常清晰明了的。就是使用一个函数来对原有类进行增强,使用装饰器的好处主要有两点:
- 增强代码可读性。装饰器的名称可以自定义,相当于一个注释,解释了装饰器的主要作用。
- 不改变原有类代码。装饰器仅仅是在类的前面加入了一行代码来扩展类的功能,对类本身的代码不做任何更改。
# 类方法的装饰
除了对类整体进行装饰,装饰器还可以单独对类中的属性进行装饰。对属性进行装饰时,装饰器接收三个参数,以下面的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++;
};