类的修饰
许多面向对象的语言都有修饰器( Decorator
)函数,用来修改类的行为。
@testable
class MyTestableClass {
// ...
}
function testable(target) {
target.isTestable = true
}
MyTestableClass.isTestable; // true
testable
函数即为修饰器,为 MyTestableClass
类添加了名为 isTestable
的静态属性。
一个修饰器可以理解为包装之后跟着的类的函数。
@decorator
class A {}
// 等同于
class A {}
A = decorator(A) || A
也就是说,修饰器是一个对类进行处理的函数。修饰器函数的第一个参数,就是所要修饰的目标类。
当然只要在 @
符号后是一个函数即可,因此为了可以生成更加灵活的修饰器,我们可以通过函数返回函数的形式来编写修饰器。
function testable(isTestable) {
return function(target) {
target.isTestable = isTestable;
}
}
@testable(true)
class MyTestableClass {}
MyTestableClass.isTestable; // true
@testable(false)
class MyClass {}
MyClass.isTestable; // false
testable
函数最终返回一个函数,符合修饰器的形式,同时通过传入不同的参数,就可以定制在不同情况下的修饰器。
方法的修饰
修饰器不仅可以修饰类,还可以修饰类的属性。
class Person {
@readonly
name() {
return `${this.first} ${this.last}`
}
}
上述代码我们为 name
方法添加了一个修饰器 readonly
, readonly
同样也是一个函数,如下
function readonly(target, name, descriptor){
// descriptor对象原来的值如下
// {
// value: specifiedFunction,
// enumerable: false,
// configurable: true,
// writable: true
// };
descriptor.writable = false;
return descriptor;
}
readonly(Person.prototype, 'name', descriptor);
// 类似于
Object.defineProperty(Person.prototype, 'name', descriptor);
相比于类修饰器,方法修饰器多了两个参数,分别为:
name
: 修饰器修饰的属性名。descriptor
: 该属性名对应属性的描述对象。
举个例子:使用 @log
修饰器,达成输出日志的目的
function log(target, name, descriptor) {
var oldValue = descriptor.value;
descriptor.value = function() {
console.log(`Calling ${name} with`, arguments);
return oldValue.apply(this, arguments);
}
return descriptor;
}
class Math {
@log
add(a, b) {
return a + b;
}
}
const math = new Math();
math.add(2, 4);
// Calling add with [2, 4];
// 6
这样就达成了在执行方法的时候,输出一条日志的目的,当然修饰器能做的事情可以有更多。
一些可以使用的修饰器的类库: