@Decorator 装饰器是 es7 的语法,这个方法对于面向切面编程有了更好的诠释,在一些情境中可以使用,比如路人 A 的代码实现了一需求,路人 B 希望用 A 的方法来实现一个新的需求,而路人 A 又不希望大改自己的代码,这时候装饰器就能派上用场了。本文就结合情境来说说 Decorator 的用法。
装饰器顾名思义就是装饰某种东西的方法,可以用来装饰属性、变量、函数、类、实例方法 … 本质上是个函数。以 @符开头,函数名称自拟。
先看这么一个类↓
@hobby
Class Person {constructor() { }
@readOnly
name = 'AAA';
@dealData
eat() {console.log('爱吃苹果')
}
}
let oP = new Person();
这个函数就用来修饰装饰对象的
function readOnly(proto, key, descriptor) {console.log(proto, key, descriptor) // 原型,'name',一个包含对 name 属性描述内容的对象
}
descriptor 是重头戏,这个对象里包含对装饰对象的描述属性
configurable: true/false, 可配置与否
enumerable: true/false, 可枚举与否
writable: true/false, 可写与否
initializer: 静态属性的 value 值
value: 非静态属性的 value 值
上面三个属性好理解,修改也方便,如下
function readOnly(proto, key, descriptor) {
// 将静态属性 name 改为只可读,不可写
descriptor.writable = false;
}
initializer: 静态属性的 value 值
value: 非静态属性的 value 值
这两个值就比较有意思了,他们俩的关系是水火不容的,静态属性的装饰器的 descriptor 里有 initializer, 而非静态属性的装饰器其 descriptor 对象里则是有 value 这个属性,没有 initializer。
function readOnly(proto, key, descriptor) {
// initializer 可以重新赋值
descriptor.initializer = function () {
// 函数返回的值就是该静态属性新的值
return 'BBB'
}
}
function dealData(proto, key, descriptor) {
// 当我们需要改变函数功能的时候,可以通过这种方式,相当于做个代理,也不会影响原函数
// 存一下原来的方法
let oldValue = descriptor.value;
// 修改 (添加) 函数的原有功能
descriptor.value = function() {console.log('爱吃橘子');
oldValue.call(this, arguments);
}
}
装饰器也是可以传参并且执行返回的函数的
Class Person {constructor() { }
@readOnly
name = 'AAA';
@dealData('AAA')
eat() {console.log('爱吃苹果')
}
}
function dealData(who) {return function (proto, key, descriptor) {
let oldValue = descriptor.value;
descriptor.value = function() {console.log(who + '爱吃橘子'); // AAA 爱吃橘子
return oldValue.call(this, arguments);
}
}
}
值得注意的一点是如果这里的 eat 函数写成箭头函数赋值的形式,就不再是原型上的方法了而是变为静态属性了,要注意一下。
Class Person {constructor() { }
@readOnly
name = 'AAA';
@dealData('AAA')
eat = ()=> {console.log('爱吃苹果')
}
}
装饰器装饰类:
@hobby
Class Person {constructor() { }
@readOnly
name = 'AAA';
@dealData
eat() {console.log('爱吃苹果')
}
}
function hobby(target) {console.log(target) // 结果是这个类本身
// 就可以通过 target 修改类的属性
target.name = 'CCC';
// 增加属性
target.age = 18;
}
被装饰的对象可以使用多个装饰器。