Open
Description
一、介绍
Decorator
,即装饰器,从名字上很容易让我们联想到装饰者模式
简单来讲,装饰者模式就是一种在不改变原类和使用继承的情况下,动态地扩展对象功能的设计理论。
ES6
中Decorator
功能亦如此,用于扩展类属性和类方法
这里定义一个学生,这时候他什么学号都没有
class student{
}
定义一个得到 学号的函数,即装饰器
function info(target){
target.num= 123
}
使用该装饰器对学生进行增强
@info
class student{
}
这时候学生就有学号了
student.num // 123
上述代码虽然简单,但也能够清晰看到了使用Decorator
两大优点:
- 代码可读性变强了,装饰器命名相当于一个注释
- 在不改变原有代码情况下,对原来功能进行扩展
二、用法
Docorator修饰对象为下面两种:
- 类的装饰
- 类属性的装饰
类的装饰
当对类
本身进行装饰的时候,能够接受一个参数
,即类本身
@decorator
class A {}
// 等同于
class A {}
A = decorator(A) || A;
下面@testable
就是一个装饰器,target
就是传入的类,即MyTestableClass
,实现了为类添加静态属性
@testable
class MyTestableClass {
// ...
}
function testable(target) {
// 添加属性
target.isTestable = true;
// 原型链上添加方法
const prototype = target.prototype;
prototype.getInfo = function(){
console.log(this.isTestable);
}
}
MyTestableClass.isTestable // true
MyTestableClass.getInfo() // true
如果想要传递参数,可以在装饰器外层再封装一层函数
function testable(isTestable) {
return function(target) {
target.isTestable = isTestable;
}
}
@testable(true)
class MyTestableClass {}
MyTestableClass.isTestable // true
@testable(false)
class MyClass {}
MyClass.isTestable // false
类属性的装饰
当对类属性进行装饰的时候,能够接受三个参数:
target
:被修饰的类
name
:类成员的名字
descriptor
:属性描述符,对象会将这个参数传给Object.defineProperty
首先定义一个readonly
装饰器
function readonly(target, name, descriptor){
descriptor.writable = false; // 将可写属性设为false
return descriptor;
}
使用readonly
装饰类的name
方法
class Person {
@readonly
name() { return `${this.first} ${this.last}` }
}
相当于以下调用
readonly(Person.prototype, 'name', descriptor);
修饰方法
function time(target, name, descriptor) {
const func = descriptor.value;
if (typeof func === 'function') {
// 装饰者模式重写func
descriptor.value = function(...args) {
console.log("在头部添加代码", new Date());
const results = func.apply(this, args);
console.log("在尾部添加代码", new Date());
return results;
}
}
}
class Person {
@time
say() {
console.log('hello')
}
}
const person = new Person();
person.say();
如果一个方法有多个装饰器,就像洋葱一样,先从外到内进入,再由内到外执行
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
外层装饰器@dec(1)
先进入,但是内层装饰器@dec(2)
先执行
注意
装饰器不能用于修饰函数,因为函数存在变量声明情况