装饰器 decorator

223 阅读3分钟

装饰器是一种特殊类型的声明,它能够被附加到类声明,方法,访问符,属性或者参数上。装饰器使用@expression 这种形式,expression 求值后必须为一个函数,他会在运行时被调用,被装饰的生命信息作为参数传入。

例如,要一个@fn 装饰器,要定义一个函数
function fn(target){
    // do somethinng.....
}

通过一些@方法名可以对一些对象进行装饰然后返回一个被包装过的对象,可以装饰的对象包括,类,对象,方法等

1,装饰器工厂

要制定装饰器首先要有一个装饰器工厂函数,装饰器工厂就是一个简单的函数,他返回一个表达式,以供装饰器在运行时是调用

function color (value: string) { //装饰器工厂
	return function (target) { //装饰器
		//do something target and value
	}
}

2,装饰器组合

多个装饰器可以应用到同一个声明上,操作步骤 A,从上之下依次对装饰器表达式求值 B,求值结果会被当做成函数,从下至上依次被调用

如果使用装饰器工厂 求值结果为:装饰器工厂函数内从上至下依次调用,装饰器内从下至上依次调用

function f ( ) {
	console.log(‘f() : evaluated’)
	return function (target, value){
		console.log(‘f(): called’)
	}
}

function g ( ) {
	console.log(‘g() : evaluated’)
	return function (target, value){
		console.log(‘g(): called’)
	}
}

Class c {
	@f()
	@g()
	method(){}
}

结果打印: 装饰器函数外部由上向下依次执行,内部由下向上依次执行
f(): evaluated
g() : evaluated
g(): called
f(): called

3,装饰器求值

类中不同声明上的装饰器求值顺序 。。。。。。略

4,类装饰器:

类装饰器在类声明之前被声明,类装饰器用于类构造函数,可以修改或替换类定义。

类装饰器表达式在运行时被当做函数调用,类的构造函数作为其唯一的参数。 装饰器的第一个函数参数就是所要要装饰的目标类 。 例如:class类构造函数就是 fn 的唯一参数。

@fn
class newFN{
    //do something...
}

如果类装饰器返回一个值,他会使用提供的构造函数类替换类的声明。

简单的装饰器
1,装饰器函数
//装饰器函数
const classDecorator = (target: { isShow: boolean; }) => {
        target.isShow = false;
}

//class 构造函数
@classDecorator
class Mine extends Component{
    isShow: string; // 声明
    constructor(){
        super()
    }
    componentDidMount(){
        //do something...
    }
    render(){
        return (
            <div>
                我的页面
            </div>
        )
    }
}
console.log(Mine.isShow) // false 装饰器传入的 boolean
export default Mine;
//一个函数不够可以外层封装一个函数。就是装饰器工厂
const classDecorator = (flag: boolean) => { //flag为调用装饰器传入的参数
    return function(target: { isShow: boolean; }) { //target 为使用该装饰器的构造类
        target.isShow = flag;
    }
}

@classDecorator(true)
class Mine extends Component{
    isShow:string;
}
console.log(Mine.isShow) //true 装饰器所传的 flag
重载构造函数
/** 
    1,类构造函数中声明未赋值,在装饰器函数中变量赋值,最终为装饰器函数返回的值
    2,类构造函数中声明且赋值,在装饰器函数中变量赋值,最终为装饰器函数返回的值
    3,类构造函数中声明且赋值,在装饰器函数中变量未赋值,最终为构造函数返回的值
**/
const classDecorator = (constructor) => {
    return class extends constructor {
        newPropoty = "new propoty"
        hello = "hello"
    }
}

@classDecorator
class Greeter {
    propoty = "propoty";
    hello: string;
    constructor(m: string){
        this.hello = m
    }
}

console.log(new Greeter('hello2')) //

前面的例子是为类添加一个静态属性,如果想添加实例属性,可以通过目标类的 prototype 对象操作。

export const classDecorator = (...message) => {
    return function(target) {
       Object.assign(target.prototype, ...message)
    }
}

const Foo = {
    foo() { console.log('foo') }
 };
 
@classDecorator(Foo)
class Mine extends Component{
    ...
}
console.log(new Mine().foo()) //foo Foo对象内部的打印

//通过classDecorator 装饰器,将对象 Foo 添加到了Mine 实例上面.

5,方法装饰器

方法装饰器在运行时会被当做函数调用,传入 3 个参数

1,类的原型对象,

2,所要装饰的属性名

3,该属性的描述对象

//装饰器
 export const decoratorFn = (target, name, descriptor) =>{
    descriptor.isShow = true;
    return descriptor;
}

class Mine extends Component {
    gretting: string;
    constructor(m){
        super(m)
        this.gretting = m;
    }
    
    //方法装饰器
    @decoratorFn
    great(){
        return 'hello,' + 'this.gretting'
    }
}

当装饰器@decoratorFn 被调用时,就会修改属性descriptor的 isShow 值