古调虽自爱,今人多不谈——装饰者模式
装饰者模式
最近在读《javascript设计模式和开发实践》,记录一下自己的理解。
定义
装者,藏也,饰者,物既成加以文采也。装饰者模式就是给对象添加更多的功能而不改变对象本身。
装饰函数
函数是javascript中的一等对象,我们给对象添加额外的方法很简单,给函数添加额外的方法,确很难。
我们通过保留原引用的方式可以还写某个函数。
var animal = function () {
console.log('动物')
}
var _animal = animal; // 保存原有的引用方式
animal = function () {
_animal();
console.log('植物')
}
animal()
这是最常见的一种做法,但是会有两个缺点。
- 需要装饰的函数变多时,会有大量的中间变量需要维护。
- this劫持
var obj = {
say: function () {
this.content()
},
content: function () {
console.log('我正在说' + this)
}
}
obj.say()
// 我正在说[object Object]
var _say = obj.say;
obj.say = function () {
console.log(this)
_say()
}
obj.say()
// Uncaught TypeError: this.content is not a function
// 根本原因是_say变量只是单纯的引用函数,不会附带函数的上下文
我们可以在函数执行的时候绑定手动绑定this。
obj.say = function () {
// _say.call(obj, arguments)
_say.bind(this)() // 这里的this指的是obj所以可以正常执行
}
obj.say()
// 我正在说[object Object]
用AOP装饰函数
AOP是面向切面编程,可以将业务逻辑各个部分隔离,降低耦合程度,提高可重用性。
Function.prototype.before = function (beforeFn) {
var _self = this; // 保存对原函数的引用 这个this指的是调用before函数的对象
return function () {
beforeFn.apply(this, arguments); // 这个this指的是当前function 所处的上下文不做特殊处理指的是window arguments也指的是当前这个匿名函数的参数
return _self.apply(this, arguments)
}
}
function personal(str) {
console.log(`我是${str}`)
}
personal('man')
personal.before(function () {
console.log(arguments)
console.log('我是人')
})('woman') // 上文中的arguments指的是现在的woman
上面的代码我们给Function的原型上添加了before方法,personal可以调用,我们在没有改变personal的前提下,personal函数有了额外的功能。
实例
-
数据上报
var log = function () { alert('开始上报数据') } var login = function () { alert('打开登录弹窗'); log() } login() // 这样写虽然可以实现但业务代码和数据统计的代码耦合在一起 var after = function (fn, afterFn) { return function () { let ret = fn.apply(this, arguments) afterFn.apply(this, arguments); return ret; } } login = function () { alert('登录') } log = function () { alert('上报') } login = after(login, log) login()
-
动态改变参数(因为before函数和_self调用的arguments是同一个,我们若将arguments指向一个对象即可改变参数)
var param = { name: '小明' } var changeName = function (param) { console.log(param.name) } changeName.before(function () { arguments[0].name='小芳'; // arguments[0]只是对param的引用两个变量指向同一个对象,故会改变name的值 })(param) // 小芳
装饰者适用场景
- 接手新项目不熟悉业务具体代码,要添加额外的功能。
- 不同业务逻辑相互交叉。
-
没事重构代码————装逼。