JS设计模式

目录

前言

单例设计模式

Command 命令模式

Constructor构造器模式

工厂模式Factory

发布订阅设计模式 publish&subscribe

观察者模式 

中介者模式


前言

JS设计模式是一种思想,更规范更合理的管理代码(方便维护,升级,扩展,开发)

单例设计模式

单例设计模式Singleton && Command 命令模式

  • 最早期的编程化思想(同样的还有:AMD/CMD/commoJS/ES6Module)
  • 避免全局变量污染,
  • 实现模块之间的相互调用(提供了模块导出方案)
    //Object
    let obj = {}

    //------高级单例------
    //公共模块 utils
    let utils = (function () {
        function throttle(func, wait) {
            //....
        }
        return {
            throttle: throttle;
        }
    })()

    // a模块
    let AModule = (function () {
        utils.throttle();
        function fn() { };
        function query() { };
        return {
            query: query
        }
    })()
    //b模块
    let BMoudle = (function () {
        utils.throttle();
        function fn() { };
        AModule.query();
        return {}
    })()

Command 命令模式

在实际的开发过程中,我们还可以基于命令模式管控方法的执行顺序,从而有效的实现出对应的功能

    //b模块(实现当前模块下需要完成的所有功能)
    let BMoudle = (function () {
        utils.throttle();
        AModule.query();

        //获取数据的
        function getData() { };
        // 绑定数据的
        function binding() { };
        //处理事件的
        function handle() { };
        //处理其他事情的
        function fn() { };
        return {
            //模块的入口(相当于模块的大脑,控制模块的顺序)
            init() {
                getData();
                binding();
                handle();
                fn();
            }
        }
    })();
    BMoudle.init()

Constructor构造器模式

自定义类和实例 ,(站在面向对象的思想上去构建项目)

私有&公有的属性和方法,

编写公共类库&写一些插件组件,

每一次调用插件我们都是创造这个类的实例,即保证了每个实例之间,有自己的私有属性,互不影响也可以保证一些方法属性还是公用的,有效的避免了代码冗余

  // ES5
    function Fn() {
        this.xxx = xxx;
    }
    Fn.prototype = {
        constructor: Fn,
        query() { },
        //..
    }
    Fn.xxx = xxx;
    //es6 
    class Fn {
        constructor() {
            this.xxx = xxx;
        }
        query() { };
        static xxx() { };
    }
    let f1 = new Fn;
    let f2 = new Fn;

工厂模式Factory

简单的工厂模式(一个方法根据传递的参数不同,做了不同的处理)

   function factory(options) {
        if (options === null) options = {};
        if (!/^(object|function)$/i.test(typeof options)) options = {};
        let { type, payload } = options;
        if (type = 'MYSQL') {
            //...
            return;
        };
        if (type = 'SQLSERVER') {
            //....
            return;
        }
    }
    factory({
        type: 'SQLSERVER',
        payload: {
            root: '',
            pass: '',
            select: ''
        }
    })

JQ中的工厂模式(加工转换)

 (function () {
        function jQuery(selector, content) {
            return new jQuery.fn.init(selector, content)
        }
        jQuery.fn = jQuery.prototype = {
            constructor: jQuery,
            //.....
        };

        //中间转换
        function init(selector, content, root) { }
        jQuery.fn.init = init;
        init.prototype = jQuery.fn;

        if (typeof window !== 'undefined') {
            window.$ = w.jQuery = jQuery
        }
    })()
    // $()--->jQuery的实例

发布订阅设计模式 publish&subscribe

自定义事件的一种方案 灵感来源于 addEventListener DOM2事件绑定,

  • 给当前元素的某一个事件行为,绑定多个不同的方法(事件池机制)
  • 当事件行为触发的时候,会依次通知事件池中的方法执行
  • 支持内置事件(标准事件,)例如 click dbclick mouseenter...

扫盲:dom0级事件绑定原理:给元素对象对应的事件行为的私有属性赋值,dom2往事件池里添加方法

简单版的发布订阅

弊端:只有一个事件池,只能应用于一个场景

 (function () {
        //自己创造的事件池
        let pond = [];

        //向事件池中注入方法
        function subscribe(func) {
            //去重处理
            if (!pond.includes(func)) {
                pond.push(func)
            }
            //每一次执行,返回的方法是用来移除当前这个新增的这个方法的
            return function unsubscribe() {
                pond = pond.filter(item => item !== func)
            }

        }
        //通知事件池中的方法执行
        subscribe.fire = function fire(...params) {
            pond.forEach(item => {
                if (typeof item === 'function') {
                    item(...params)
                }
            })
        }
        window.subscribe = subscribe
    })();
    let unsubscribe1 = subscribe(function () {
        console.log(1, arguments);
    });
    subscribe(function () {
        console.log(2, arguments);
    });
    subscribe(function () {
        console.log(3);
        unsubscribe1();
    });
    subscribe(function () {
        console.log(4);
    });
    setTimeout(() => {
        subscribe.fire(10, 20, 30)
    }, 1000)
    setTimeout(() => {
        subscribe.fire(10, 20, 30)
    }, 2000)

应用场景:凡是某个阶段到达的时候,需要执行很多方法(更多的时候,到底执行多少个方法不确定,需要边写业务,边处理),我们可以基于发布订阅模式来管理代码:

  • 创建事件池->发布计划
  • 向事件池中加入方法-->向计划表中订阅任务
  • fire-->通知计划表中的任务执行

2.一个项目中,我们可能会出现多个事情都需要基于发布订阅来管理,一个事件池不够

@1管理多个事件池

基于面向对象 类(subscribe,unsubscribe,fire公用的)&实例,(每个实例都有一个自己私有的事件池)

 class Sub {
        //实例私有的属性:私有的事件池
        pond = [];
        //原型上设置方法:向事件池中订阅任务
        subscribe(func) {
            let self = this,
                pond = self.pond;
            if (!pond.includes(func)) pond.push(func);
            //每一次执行,返回的方法是用来移除当前这个新增的这个方法的
            return function unsubscribe() {
                let i = 0,
                    len = pond.length,
                    item = null;
                for (; i < len; i++) {
                    item = pond[i];
                    if (item === func) {
                        pond.splice(i, 1)
                        break;
                    }
                }
            }
        };
        //通知当前实例所属事件池中的方法执行
        fire(...params) {
            let self = this,
                pond = self.pond;
            pond.forEach(item => {
                if (typeof item === 'function') {
                    item(...params)
                }
            })
        };

    }
    let sub1 = new Sub;
    sub1.subscribe(function () {
        console.log(1, arguments);
    })
    sub1.subscribe(function () {
        console.log(2, arguments);
    })
    setTimeout(() => {
        sub1.fire(100, 200)
    }, 1000)

    let sub2 = new Sub;
    sub2.subscribe(function () {
        console.log(1, arguments);
    })
    sub2.subscribe(function () {
        console.log(2, arguments);
    })
    setTimeout(() => {
        sub2.fire(200, 300)
    }, 2000)

@2一个事件池支持不同的事件池类型

 //type事件类型, func方法
    let sub = (function () {
        let pond = {};
        //向时间池中追加指定自定义事件类型的方法
        const on = function on(type, func) {
            //验证增加的时候,验证当前类型在事件池中是否已经存在
            !Array.isArray(pond[type]) ? pond[type] = [] : null;
            let arr = pond[type];
            if (arr.includes(func)) return;
            arr.push(func);
        };
        //从事件池中移除指定自定义事件类型的方法
        const off = function off(type, func) {
            let arr = pond[type],
                i = 0,
                item = null;
            if (!Array.isArray(arr)) throw TypeError(`${type} 自定义事件类型并不存在`);
            for (; i < arr.length; i++) {
                item = arr[i];
                if (item == func) {
                    //移除掉
                    //splice改变原数组,导致数组塌陷(删除数组索引发生了变化,在循环会导致这个问题)
                    // arr.splice(i, 1)
                    arr[i] = null;//这样只是让集合中当前项值变为null,但是集合中的结构不会发生变化(索引不变),下次执行emit的时候,遇到当前项是null,我们在去把其移除掉即可
                    break;
                }
            }
        };
        //通知事件池中定自定义事件类型的方法执行
        const emit = function emit(type, ...params) {
            let arr = pond[type],
                i = 0,
                item = null;
            if (!Array.isArray(arr)) throw TypeError(`${type} 自定义事件类型并不存在`);
            for (; i < arr.length; i++) {
                item = arr[i];
                if (typeof item === 'function') {
                    item(...params)
                    continue;
                }
                //不是函数把它移除掉即可
                arr.splice(i, 1);
                i--;
            }

        };

        return {
            on,
            off,
            emit
        };
    })()
    const fun1 = () => console.log(1)
    const fun2 = () => {
        console.log(2)
        sub.off('A', fun1)
    }
    const fun3 = () => console.log(3)
    const fun4 = () => console.log(4)
    const fun5 = () => console.log(5)
    const fun6 = () => console.log(6)

    sub.on('A', fun1);
    sub.on('A', fun2);
    sub.on('A', fun3);
    setTimeout(() => {
        sub.emit('A')
    }, 1000)


    sub.on('B', fun4);
    sub.on('B', fun5);
    sub.on('B', fun6);
    setTimeout(() => {
        sub.emit('B')
    }, 2000)
    setTimeout(() => {
        sub.emit('A')
    }, 3000)

观察者模式 

目标:管理观察者: 增加/删除观察者 通知观察者中update方法执行(且传递消息notfily)

每一个观察者有update方法接收传的消息并处理

//定义观察者,形式可以不一样,只需要具备update方法
    class OB {
        update(msg) {
            console.log(`我是观察者1,我接受到的信息是${msg}`);
        }
    }
    let DEMO = {
        update(msg) {
            console.log(`我是观察者2,我接受到的信息是${msg}`);
        }
    };
    //目标:管理观察者: 增加/删除观察者 通知观察者中update方法执行(且传递消息notfily)
    class Subject {
        observerList = [];
        add(observer) {
            this.observerList.push(observer)
        };
        remove(observer) {
            this.observerList = this.observerList.filter(item => item !== observer)
        };
        notify(...params) {
            this.observerList.forEach(item => {
                if (item && typeof item.update === "function") {
                    item.update(...params)
                }
            })
        }
    }
    let sub = new Subject;
    sub.add(new OB)
    sub.add(DEMO)
    setTimeout(() => {
        sub.notify('hello word')
    }, 1000);

中介者模式

    let mediator = (function () {
        let topics = [];
        const subscribe = function subscribe(callback) {
            topics.push(callback)
        };
        const publish = function publish(...params) {
            topics.forEach(callback => {
                if (typeof callback === 'function') {
                    callback(...params)
                }
            })
        };

        return {
            subscribe,
            publish
        }
    })()
    mediator.subscribe(() => console.log(1))
    mediator.subscribe(() => console.log(2))
    setTimeout(() => {
        mediator.publish()
    }, 1000)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值