Decorator 修饰器
说明
由 ES2017 引入了这个功能,他是一个函数用来修改类的行为,解决了不同类之间共享方法的问题
修饰器对类的行为的改变是在代码编译时发生的,而不是在运行时,本质就是编译时执行的函数
由于存在变量提升,使得修饰器不能用于函数(包括构造函数),类不会提升所以不会有这个问题
基本使用
@decorator
class Test {
constructor(name) {
this.name = name;
}
}
function decorator (target) {
// target 就是需要被修饰的类
target.prototype.getName = function () {
console.log(this.name);
}
}
const test = new Test('xiaobai');
test.getName(); // xiaobai
修饰器通过封装后接受其他参数
function addMethod(methods) {
return function (target) {
Object.assign(target.prototype, methods);
}
}
const methods = {
change: function () {console.log('change')},
add: function () {},
delete: function () {},
get: function () {}
}
@addMethod(methods)
class Test {}
const test = new Test();
test.change(); // change
修饰器可以修饰类中的方法
作用在方法上的 decorator 接受的第一个参数是类的 prototype;作用到类上的 decorator 第一个参数表示类本身
Decorators 的本质是利用了 ES5 的 Object.defineProperty 属性,所以它的三个参数:target name descriptor 跟 Object.defineProperty 的参数是一致的
Object.defineProperty 的参数详解看这里
- target:需要修饰的类的原型 prototype
- name: 需要修饰的类的属性名或者方法名
- descriptor:被修改的属性的描述符
- configurable:true | false 表示对象的属性是否可以被删除,以及除 writable 特性外的其他特性是否可以被修改
- enumerable:true | false 定义被修改的属性是否可以是可以枚举的
- value:此属性对应的值(数值、对象、函数等)
- writable:true | false 是否可以重写此属性
- initializer:如果修饰器修饰的是一个属性不是方法,则会有这个属性出现代替 value
function log (target, name, descriptor) {
// 保留旧的方法
const tempFn = descriptor.value;
// 重新定义方法
descriptor.value = function () {
console.log(`this is ${name} arguments: `, arguments);
// 借用旧的方法
return tempFn.apply(this, arguments);
}
return descriptor;
}
class Test {
constructor() {
this.list = []
}
@log
add (...items) {
this.list.push(...items);
}
}
var test = new Test();
test.add(12, 33, 222);
// 修饰器打印的日志
// this is add arguments: (3) [12, 33, 222, callee: (...), Symbol(Symbol.iterator): function]
console.log(test); // {list: [12, 33, 222]}
修饰器修饰属性
function change(target, name, descriptor) {
const tempVal = descriptor.initializer();
console.log(descriptor.initializer);
/*
initializer 函数类型
function initializer() {
return 'def value';
}
*/
descriptor.initializer = function () {
return 'change ' + tempVal;
}
return descriptor;
}
class Test {
@change
def = 'def value'
}
const test = new Test();
console.log(test.def) // change def value
修饰器可以叠加使用
function log (order) {
return function (target, name, descriptor) {
const tempFn = descriptor.value;
descriptor.value = function () {
console.log('decorator ' + order);
return tempFn.apply(this, arguments);
}
return descriptor;
}
}
class Test {
constructor() {
this.list = []
}
@log('top')
@log('bottom')
add (...items) {
this.list.push(...items);
}
}
var test = new Test();
test.add(12, 33, 222);
// decorator top | decorator bottom
console.log(test); // {list: [12, 33, 222]}