装饰者模式
在提及这个新特性前我们先了解一下设计模式中的装饰者模式。
定义
装饰者模式能够在不改变对象自身的基础上,在程序运行期间给对像动态的添加职责。与继承相比,装饰者是一种更轻便灵活的做法。
装饰者模式 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提供的方法,在浏览器中是无法直接运行的,所以我们需要提前作准备,对它进行编译。
- 安装基础插件
npm i babel-plugin-transform-decorators-legacy babel-register --save-dev
安装:
babel-plugin-transform-decorators-legacy
babel-register
transform-decorators-legacy:
是第三方插件,用于支持decorators
babel-register:
用于接入node api
编写你的装饰器栗子decorator.js
创建compile.js
require('babel-register')({
plugins: ['transform-decorators-legacy']
});
require("./decorator.js")
- 运行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}`);