基于装饰器——我劝你不要在业务代码上装逼!!!
装饰器模式的定义
- • 在传统的面向对象语言中,给对象添加功能常使用继承的方式,但继承的方式并不灵活,会带来一些许多问题,如:超类和子类存在强耦合性,也就是说当改变超类时,子类也需要改变。
- • 而装饰器模式的出现改变的这种方式,
装饰器模式可在不改变现有对象解构的基础上,动态地为对象添加功能
。
传统的 JavaScript 装饰器
var plane = { fire: function () { console.log("普通子弹"); }, }; var missleDecorator = function () { console.log("发射导弹"); }; var atomDecorator = function () { console.log("发射原子弹"); }; var fire1 = plane.fire; plane.fire = function () { fire1(); missleDecorator(); }; var fire2 = plane.fire; plane.fire = function () { fire2(); atomDecorator(); }; plane.fire(); /** 普通子弹 发射导弹 发射原子弹 */
装饰函数
- • 在 JavaScript 中,几乎一切都是对象,其中函数也被成为对象,在平时的开发中,我们都在和函数打交道。在给对象扩展属性和方法时,很难在不改动原功能函数的情况下,给函数添加一些额外的功能,最直接的粗暴方式就是直接改写函数,但这是最差的方式,这违反了开放——封闭原则。
- • 如下:
function a(){ console.log(1); } // 改写: function a(){ console.log(1); // 新功能 console.log(2); }
- • 很多时候,我们都不想去触碰之前的一些代码,但需要添加功能,所以如果需要在不改变原功能函数的情况下,给函数添加功能。可使用以下方式:
- • 要想完美的给函数添加功能,可使用
AOP 来装饰函数
。
- • AOP:一种编程规范,通过将关注点从主业务逻辑中剥离出来并单独处理,以此来提高代码的可读性和重用性。
- • 如下:
Function.prototype.before = function (beforeFn) { var _self = this; return function () { beforeFn.apply(this, arguments); return _self.apply(this, arguments); }; }; Function.prototype.after = function (afterFn) { var _self = this; return function () { var ret = _self.apply(this, arguments); afterFn.apply(this, arguments); return ret; } } // before 和 after 函数都接收一个函数作为参数,这个函数也就是新添加的函数(里面也就是要添加的新功能逻辑)。 // 而before 和 after 函数区别在于在是原函数之前执行还是之后执行。
- • AOP 函数的使用
Function.prototype.before = function (beforeFn) { var _self = this; return function () { beforeFn.apply(this, arguments); return _self.apply(this, arguments); }; }; Function.prototype.after = function (afterFn) { var _self = this; return function () { var ret = _self.apply(this, arguments); afterFn.apply(this, arguments); return ret; } } var o1 = function(){ console.log('1'); } var o2 = function(){ console.log('2'); } var o3 = function(){ console.log('3'); } var desctor = o1.after(o2); desctor = desctor.after(o3); desctor(); // 1 2 3 /** var desctor = o1.after(o2); desctor = desctor.after(o3); desctor(); 1 2 3 var desctor = o1.before(o2); desctor = desctor.before(o3); desctor(); 3 2 1 var desctor = o1.after(o2); desctor = desctor.before(o3); desctor(); 3 1 2 var desctor = o1.before(o2); desctor = desctor.after(o3); desctor(); 2 1 3 */
AOP的应用
1.数据上报
- • 在程序开发中,当业务代码开发完后,在结尾时需要加很多的日志上报的代码,普遍我们会去改已经之前封装好的功能函数。其实这并不是一个好的方式,那如何在不直接修改之前函数的基础上添加日志上报功能呢?
- • 如下:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <title>AOP日志上报</title> <meta http-equiv="X-UA-Compatible" content="IE=edge" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <script src="https://unpkg.com/vue@3.2.20/dist/vue.global.js"></script> </head> <body> <div id="app"> <button class="btn" @click="handler">Button</button> <p id="tt">{{message}}</p> </div> </body> </html> <script type="text/javascript"> // log report const { reactive, ref, createApp } = Vue; const app = createApp({ setup() { const message = ref("未点击"); const count = ref(0); Function.prototype.before = function (beforeFn) { var _self = this; return function () { beforeFn.apply(this, arguments); return _self.apply(this, arguments); }; }; Function.prototype.after = function (afterFn) { var _self = this; return function () { var ret = _self.apply(this, arguments); afterFn.apply(this, arguments); return ret; }; }; function handler() { message.value = `已点击${++count.value}`; } handler = handler.after(log); function log() { message.value = message.value + "-----> log reported"; console.log("log report"); } return { message, handler, }; }, }); app.mount("#app"); </script>