例子1:纯粹的装饰模式(对方法进行装饰)
纯粹的装饰模式指的是不增加对原有类的接口,只是对原有类的方法进行装饰而已。
function decorateArmour(target, key, descriptor) {
const method = descriptor.value;
//这里的method指的是init方法
let moreDef = 100;
let ret;
//对我们默认的init方法进行装饰,重新赋值。这里的...args表示我们在调用init方法时候传入的参数,即对参数进行了截获,然后进行处理
descriptor.value = (...args)=>{
args[0] += moreDef;
ret = method.apply(target, args);
//对截获的参数进行处理,从而继续调用我们的init方法。第一个参数表示this(其值为Man.prototype),而第二个参数表示传入init方法的参数,而且这个参数已经被装饰过了
return ret;
}
return descriptor;
}
class Man{
constructor(def = 2,atk = 3,hp = 3){
this.init(def,atk,hp);
}
@decorateArmour
init(def,atk,hp){
this.def = def; // 防御值
this.atk = atk; // 攻击力
this.hp = hp; // 血量
}
toString(){
return `防御力:${this.def},攻击力:${this.atk},血量:${this.hp}`;
}
}
var tony = new Man();
console.log(`当前状态 ===> ${tony}`);
// 输出:当前状态 ===> 防御力:102,攻击力:3,血量:3
这里有几个地方需要注意:
(1)decorateArmour方法中第一个参数target指的是Man这个class的原型对象,你可以直接运行"node decorator1.compiled.js",你会发现我们可以执行输出target.constructor为如下的函数:
function Man() {
var def = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 2;
var atk = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 3;
var hp = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 3;
_classCallCheck(this, Man);
this.init(def, atk, hp);
}
(2)decorator函数中第二个参数表示我们要包装的这个函数的函数名称
(3)decorator函数中第三个参数表示我们要包装的这个函数的描述符,如下所示:
{ value: [Function: init],
writable: true,
enumerable: false,
configurable: true
}
如果我们要对函数进行装饰,其实就是对descriptor.value进行处理而已,因为writable,enumerable,configurable只是对于这个函数的描述,而value才是调用init函数的时候执行的方法。
(4)某一个方法中可以同时经过多个decorator的装饰,而且互不干扰,从而对原类的侵入性非常小
例子2:对类进行装饰 下面的装饰器直接作用于类本身。
function decorateArmour(target, key, descriptor) {
const method = descriptor.value;
let moreDef = 100;
let ret;
descriptor.value = (...args)=>{
args[0] += moreDef;
ret = method.apply(target, args);
return ret;
}
return descriptor;
}
function addFly(canFly){
return function(target){
//使用装饰器对类进行装饰,那么我们的target就是类本身
target.canFly = canFly;
let extra = canFly ? '(技能加成:飞行能力)' : '';
let method = target.prototype.toString;
//获取类的prototype.toString方法,并且对该方法进行装饰
target.prototype.toString = (...args)=>{
return method.apply(target.prototype,args) + extra;
}
return target;
}
}
@addFly(true)
class Man{
constructor(def = 2,atk = 3,hp = 3){
this.init(def,atk,hp);
}
@decorateArmour
init(def,atk,hp){
this.def = def;
// 防御值
this.atk = atk;
// 攻击力
this.hp = hp;
// 血量
}
}
var tony = new Man();
console.log(`当前状态 ===> ${tony}`);
这里要注意:我们在对类进行装饰的时候传入的target就是类本身,即Man这个函数(babel编译后为一个函数)。
参考资料: