1

@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;
}

被装饰的对象可以使用多个装饰器。


CelesteW
11 声望4 粉丝

不想当产品经理的画师不是好程序员(ˉ▽ ̄~)