手记

装饰者模式 && ES7 decorator装饰器

装饰者模式

在提及这个新特性前我们先了解一下设计模式中的装饰者模式。

定义

装饰者模式能够在不改变对象自身的基础上,在程序运行期间给对像动态的添加职责。与继承相比,装饰者是一种更轻便灵活的做法。

装饰者模式 v.s. 适配器模式

装饰者模式和适配器模式都是包装模式 (Wrapper Pattern),它们都是通过封装其他对象达到设计的目的的,但是它们的形态有很大区别。

适配器模式我们使用的场景比较多,比如连接不同数据库的情况,你需要包装现有的模块接口,从而使之适配数据库 —— 好比你手机使用转接口来适配插座那样;

装饰者模式不一样,仅仅包装现有的模块,使之 “更加华丽” ,并不会影响原有接口的功能 —— 好比你给手机添加一个外壳罢了,并不影响手机原有的通话、充电等功能;

装饰者模式使用场景

  • AOP(面向切面编程)

    • 使用场景:安全检查、缓存、调试、持久化

  • 封装before/after函数

  • 封装axios添加参数,鉴权,错误监听等功能

  • 为表单提交添加验证函数

装饰器decorator

  • 装饰器可以改变类的行为,也可以改变方法的行为。

  • 装饰器不能用于函数,因为存在函数提升问题。

  • 装饰器接受三个参数,因为它本质上是利用了 ES5 的 Object.defineProperty 属性,这三个参数其实是和 Object.defineProperty 参数一致的,因此不能更改。

    • 第一个参数是要装饰的对象target

    • 第二个参数是对象的属性名key

    • 第三个参数是该属性的描述对象descriptor

// descriptor对象{
    value: specifiedFunction,
    enumerable: false,
    configurable: true,
    writable: true};
  • 如果同一个方法有多个修饰器,会像剥洋葱一样,先从外到内进入,然后由内向外执行。

立即开始

由于decorator是es7提供的方法,在浏览器中是无法直接运行的,所以我们需要提前作准备,对它进行编译。

  1. 安装基础插件

npm i babel-plugin-transform-decorators-legacy babel-register --save-dev

安装:
babel-plugin-transform-decorators-legacy 
babel-registertransform-decorators-legacy:
是第三方插件,用于支持decorators

babel-register:
用于接入node api
  1. 编写你的装饰器栗子decorator.js

  2. 创建compile.js

require('babel-register')({    plugins: ['transform-decorators-legacy']
});require("./decorator.js")
  1. 运行compile.js

node compile

一个decorator栗子

// 主食加餐-方法装饰器function addMeat(target, key, descriptor) {    const method = descriptor.value;    let ret;
    descriptor.value = (...args) => {
        args[0] = '鳗鱼炒饭';
        ret = method.apply(target, args);        return ret;
    }    return descriptor
}// 饮料加餐-方法装饰器function addDrink(target, key, descriptor) {    const method = descriptor.value;    let ret;
    descriptor.value = (...args) => {
        args[1] = '莫吉托';
        ret = method.apply(target, args);        return ret;
    }    return descriptor
}// 新增甜品-类装饰器function addDimSum(isAdd) {    return function(target) {
        target.isAdd = isAdd;        let extra = isAdd ? '  (加餐加餐!另送一份拿破仑蛋糕' : '';        let method = target.prototype.toString;
        target.prototype.toString = (...args) => {            return method.apply(target.prototype, args) + extra
        }        return target
    }
}

@addDimSum(true)class Dinner {    constructor(meat, drink) {        this.init(meat, drink);
    }

    @addMeat
    @addDrink
    init (meat, drink) {        this.meat = meat;        this.drink = drink
    }

    toString () {        return `今日特价晚餐:${this.meat} 配 ${this.drink}`;
    }
}let todayDinner = new Dinner('白饭', '白开水');console.log(`${todayDinner}`);

自己实现一个decorator

// 创建一个基类function Dinner () {    this.meat = '白饭';    this.drink = '白开水';
}

Dinner.prototype.toString = function () {    return `今日特价晚餐:${this.meat} 配 ${this.drink}`;
}// 创建一个装饰器var Decorator = function (dinner) {    this.dinner = dinner;
}

Decorator.prototype.toString = function () {    return this.dinner.toString();
}// 继承装饰器var AddMeat = function (dinner) {
    dinner.meat = '鳗鱼草饭';
    Decorator.call(this, dinner);
}

AddMeat.prototype = new Decorator();

AddMeat.prototype.toString = function () {    return this.dinner.toString();
}// 实例var todayDinner = new Dinner('白饭');
todayDinner = new AddMeat(todayDinner);console.log(`${todayDinner}`);



作者:Eason_Wong
链接:https://www.jianshu.com/p/605d822a95db


1人推荐
随时随地看视频
慕课网APP