设计模式-装饰器模式

340 阅读3分钟

小知识,大挑战!本文正在参与“程序员必备小知识”创作活动

装饰器模式是指在不改变其原有结构和功能的前提下为对象添加新的功能的模式,比如买了一个毛坯房对其进行装修,然后可以在内饰上增加一些炫酷的灯光、加上一些智能设备,这个过程中我们并不会对房子原有的结构和功能进行修改,只是在其原本的基础之上按照我们的意愿增加了一些装饰

装饰器

装饰器模式在JS的常见使用场景便是装饰器,目前JS中也已经支持装饰器

装饰器(Decorator)是一种与类(class)相关的语法,用来注释或修改类和类方法。许多面向对象的语言都有这项功能,目前有一个提案将其引入了 ECMAScript。

类的装饰

装饰器可以用来装饰整个类

function decoratorAnimal(target) {
    target.prototype.name = "animal"; // 增加实例属性
    target.prototype.eat = function () { // 增加实例方法
        console.log("吃东西了~~~");
    };
}

@decoratorAnimal
class Animal {
    constructor() {}
}

const animal = new Animal();
console.log(animal.name); // animal
animal.eat(); // 吃东西了~~~

函数decoratorAnimal的参数target就是被修饰的类的构造函数本身,在其prototype上增加属性和方式,其实就是在Animal这个类上面增加属性和方法

注意,装饰器对类的行为的改变,是代码编译时发生的,而不是在运行时。这意味着,装饰器能在编译阶段运行代码。也就是说,装饰器本质就是编译时执行的函数。

实例属性和方法的装饰

装饰器不仅可以装饰类,还可以装饰类的属性。

function decoratorSleep(target, key, descriptor) {
    descriptor.enumerable = true
    return descriptor
}

function decoratorName(target, key) {
    descriptor.protoName = 'protoName';
}

class Animal {
    @decoratorSleep
    sleep() {}

    @decoratorName
    name = "nordon";
}

函数的装饰器decoratorSleep接受三个参数

targe:等同于 Animal.prototype, 就是当前装饰类的原型对象

Key:等同于 sleep,代表所要装饰的属性名

descriptor:代表第二个参数的属性描述对象,这个对象如下

{
    value: specifiedFunction,
    enumerable: false,
    configurable: true,
    writable: true
}

因此上述装饰器等同于

decoratorSleep(Animal.prototype, 'sleep', {
    value: specifiedFunction,
    enumerable: false,
    configurable: true,
    writable: true
})

其实这其中使用频率最高的参数就是descriptor,也就是我们常说的属性描述对象,经常使用的方式是:Object.defineProperty

因此上述代码也可以表示为:

Object.defineProperty(Animal.prototype, 'sleep', {
    value: specifiedFunction,
    enumerable: false,
    configurable: true,
    writable: true
});

实例属性装饰时:descriptor.protoName赋值等同于Animal.prototype.protoName赋值

静态属性和方法的装饰

function decoratorEat(target, key, descriptor) {
}

function decoratorName(target, key) {
    descriptor.staticName = 'staticName';
}

class Animal {
    @decoratorName
    static name = "nordon";

    @decoratorEat
    eat() {}
}

target:和实例装饰时不一样,指向当前装饰类的构造函数

key和descriptor 含义一致

实现readonly

利用装饰器修饰类的属性,让类的属性只读,不可以修改

function readonly(target, key) {
    Object.defineProperty(target, key, {
        writable: false
    })
}

class Animal {
    @readonly
    age = 12;
}

const animal = new Animal();
console.log(animal.age);
animal.age = 18;

上述代码的输出可以在控制台正常输出, 但是当想要将其值修改为18时,会发现修改失败,因为writable置为false

利用装饰器可以帮我们在开发过程中额外处理许多事情,需要优秀的库也都在使用,比如mobx