js设计-装饰者模式

装饰者模式

定义:装饰者(decorator)模式能够在不改变对象自身的基础上,在程序运行期间给对象动态的添加职责。
装饰者用于通过重载方法的形式添加新功能,该模式可以在被装饰者前面或者后面加上自己的行为以达到特定的目的。
与继承相比,装饰者是一种更轻便灵活的做法。

普通对象被装饰者包裹起来,就形成了装饰者模式。
举例:
雷霆战机(吃道具的例子)

雷霆战机(吃道具的例子)

介绍:现在我们假设正在开发一个小游戏–雷霆战机,
最开始我们使用最渣的飞机,只能发射普通子弹;
吃一颗星,可以发射普通子弹和发射散弹 ;
再吃一颗,可以发射普通子弹和散弹和跟踪导弹。
一级飞机

1
2
3
4
5
6
var plane = {
fire: function(){
console.log('发射普通子弹');
}
}
plane.fire(); // '发射普通子弹'

二级飞机

1
2
3
4
5
6
7
8
9
var fire1 = plane.fire;
var shot = function() {
console.log('发射散弹');
};
plane.fire = function () {
fire1();
shot();
};
plane.fire(); // '发射普通子弹' '发射散弹'

三级飞机

1
2
3
4
5
6
7
8
9
var fire2 = plane.fire;
var track = function() {
console.log('发射跟踪导弹');
};
plane.fire = function () {
fire2(); // fire1(),shot()
track();
};
plane.fire(); // '发射普通子弹' '发射散弹' '发射跟踪导弹'

这样给对象动态的增加职责的方式就没有改变对象自身,一个对象放入另一个对象就形成了一条装饰链(一个聚合对象), 而上面的shot和track也就是装饰者、装饰函数 ,当函数执行时,会把请求转给链中的下一个对象。

装饰函数, 很多时候我们在修改别人的代码时,会改动window.onload里的东西,但是直接修改别人的代码太多,容易出错,所以我们可以用下面这个方式来修改,通过以下方式,我们可以很轻松的在onload的函数里插入自己的方法,而不会影响别人的方法

1
2
3
4
5
6
7
8
9
   // 别人的代码
window.onload = function () {
console.log('别人的代码')
}
var _onload = window.onload || function() {}
window.onload = function(){
_onload()
console.log('自己的代码')
}

面向切面的AOP编程,因为函数在js里是一等对象,所以,我们可以给函数原型添加我们需要执行的方法

在原函数之前执行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
Function.prototype.before = function (fn) {
var self = this // 原函数
return function () { // 返回一个新函数,该函数先执行fn函数,再执行原函数
fn.apply(this, arguments) // fn函数
return self.apply(this, arguments) // 原函数
}
}
var f1 = function () {
console.log(2)
}
var f1 = f1.before(function () {
console.log(1)
})
f1() // 1 2

仔细观察上面的方法,我们可以看到,fn函数和原函数使用的都是一个arguments,所以,我们可以在fn函数里修改arguments,使原函数直接使用修改过的arguments,例如

1
2
3
4
5
6
7
8
9
10
11
var obj = {
a: 'a'
}

var f1 = function (params) {
console.log(params)
}
var f1 = f1.before(function (params) {
params.b = 'b' // 给obj添加b属性
})
f1(obj) // {a: "a", b: "b"}

我们添加了一个obj,可以看到,before里,给obj添加了一个b属性,因为before里的fn和f1用的是同一个参数

在原函数之后执行

1
2
3
4
5
6
7
8
Function.prototype.after = function(afterfn) {
var _this = this;
return function() {
var ret = _this.apply(this,arguments);
afterfn.apply(this,arguments);
return ret;
};
};

封装成单独函数(不污染原型)

在原函数之前执行

1
2
3
4
5
6
7
8
var before = function(fn, before) {
return function() {
before.apply(this, arguments);
return fn.apply(this, arguments);
};
};
// 使用
before(func1, func2);

在原函数之后执行

1
2
3
4
5
6
7
8
9
var after = function(fn, after) {
return function() {
var ret = fn.apply(this,arguments);
after.apply(this,arguments);
return ret;
};
};
// 使用
after(func1, func2);

Buy me a cup of coffee,thanks!