关于javascript:基于装饰器我劝你不要在业务代码上装逼

4次阅读

共计 6596 个字符,预计需要花费 17 分钟才能阅读完成。

基于装璜器——我劝你不要在业务代码上装逼!!!

装璜器模式的定义

  • 在传统的面向对象语言中,给对象增加性能常应用继承的形式,但继承的形式并不灵便,会带来一些许多问题,如:超类和子类存在强耦合性,也就是说当扭转超类时,子类也须要扭转。
  • 而装璜器模式的呈现扭转的这种形式,装璜器模式可在不扭转现有对象解构的根底上,动静地为对象增加性能

传统的 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>

    2. 动静参数

  • 在日常开发中,咱们须要向后盾接口发送申请来获取信息,例如传参如下。业务在后续时须要增加新参数,每个接口须要把 token 值也一并传过来, 广泛咱们会去改封装的申请办法,把 token 参数增加进去。但咱们间接批改封装好的申请办法不是好的行为,那咱们可应用下面说过的 AOP 形式来改良。

    {
    name: 'xxxx',
    password: 'xxxx',
    }
  • 如下:

    <!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">{{message}}</div>
    </body>
    </html>
    
    <script type="text/javascript">
    const {reactive, ref, createApp} = Vue;
    const app = createApp({setup() {const message = ref("empty params");
            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 ajax(type, url, params){message.value = `${type} ----> ${url} -----> ${JSON.stringify(params)}`;
            }
    
            function getToken(){
                // do something
                return 'token';
            }
    
            ajax = ajax.before(function(type, url, params){params.token = getToken();
            })
    
            ajax('get', 'https://www.baidu.com/userinfo', {name: 'se', password: 'xsdsd'});
            return {message,};
        },
    });
    app.mount("#app");
    </script>

    3. 表单校验

  • 在日常开发中,咱们常常要去做校验表单数据,通常的形式是在性能函数中进行判断解决或将判断逻辑提取为一个函数的形式。但这种形式其实是与功能性函数相混合,且校验逻辑与功能性函数有耦合关系。那咱们可应用 AOP 形式来改良。
  • 如下:

    <!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">
            <label>
                姓名:<input
                    type="text"
                    v-model="data.name"
                    placeholder="请输出姓名"
                />
            </label>
            <label>
                明码:<input
                    type="text"
                    v-model="data.pass"
                    placeholder="请输出明码"
                />
            </label>
            <p v-if="data.name || data.pass">{{data.name + '/' + data.pass}} ----after-----> {{data.message}}</p>
            <hr>
            <button @click="submitBtn">submit</button>
        </div>
    </body>
    </html>
    
    <script type="text/javascript">
    const {reactive, ref, createApp, watchEffect} = Vue;
    const app = createApp({setup() {
            const data = reactive({
                name: "",
                pass: "",
                message: "",
            });
    
            Function.prototype.before = function (beforeFn) {
                var _self = this;
                return function () {if (beforeFn.apply(this, arguments) === false) return;
                    return _self.apply(this, arguments);
                };
            };
    
            function valid() {if (!data.name || !data.pass) {alert("用户名或明码不能为空");
                    return false;
                }
            }
    
            function formSubmit() {console.log("data ------>", data);
                data.message = `${data.name} ------- ${data.pass}`;
            }
    
            formSubmit = formSubmit.before(valid);
    
            function submitBtn() {formSubmit();
            }
            return {
                data,
                submitBtn,
            };
        },
    });
    app.mount("#app");
    </script>

装璜器模式的优缺点

  • 长处:

    1. 扩展性强:装璜器模式容许在不批改现有代码的状况下,动静地增加新性能或批改现有性能。通过应用装璜器,能够在运行时按需组合和重叠装璜器对象,实现各种组合形式,从而实现更多的性能扩大。
    2. 遵循开闭准则:装璜器模式通过增加装璜器类来扩大性能,而不是批改现有的代码。这样能够放弃原有代码的稳定性,合乎开闭准则,即对扩大凋谢,对批改敞开。
    3. 拆散关注点:装璜器模式将性能的扩大和外围性能拆散开来,每个装璜器类只关注繁多的额定性能。这样能够使代码更加清晰、可读性更高,并且容易保护和测试。
  • 毛病:

    1. 减少复杂性:应用装璜器模式会减少额定的类和对象,引入了更多的复杂性和层次结构。这可能使代码变得更加简单,了解和调试起来可能更加艰难。
    2. 潜在的性能影响:因为装璜器模式波及多个对象的组合和重叠,可能会引入额定的运行时开销,对性能产生肯定的影响。尤其是当装璜器链较长时,可能会导致性能降落。

装璜器模式的实用场景

  1. 动静地扩大对象性能:当须要在运行时动静地为对象增加额定的性能或责任时,装璜器模式是一个很好的抉择
  2. 遵循开闭准则:如果你心愿在不批改现有代码的状况下扩大性能,而且要放弃代码的稳定性,装璜器模式是一个适合的解决方案。
  3. 拆散关注点:当你心愿将不同的性能拆散开来,使每个性能都有本人独立的装璜器类时,装璜器模式是有用的。每个装璜器只关注繁多的额定性能,这样能够使代码更加清晰、可读性更高,并且容易保护和测试。
  4. 多层次的性能组合:如果你须要实现多个性能的组合,而且每个性能都能够灵便抉择是否增加,装璜器模式能够很好地满足这个需要。通过重叠多个装璜器对象,能够依照特定的程序组合性能,实现各种组合形式。
  5. 继承关系的代替计划:当你面临相似于创立大量子类的状况时,装璜器模式能够作为继承关系的代替计划。通过应用装璜器模式,能够防止创立过多的子类,而是通过组合不同的装璜器来实现不同的性能组合。
Tip: 文章局部内容参考于 曾探 大佬的《JavaScript 设计模式与开发实际》。文章仅做集体学习总结和常识汇总
正文完
 0