美文网首页
JS - 开发中的设计模式

JS - 开发中的设计模式

作者: 饮杯梦回酒 | 来源:发表于2019-06-08 17:04 被阅读0次

    导读:

    日常开发中,一些特定的场景下你的处理方法可能并不是很理想,往往这时借助一些设计模式可以让你优雅而高效的实现这些逻辑,下面就介绍一些虽然不是最全的但一定是最常用的设计模式。

    单例模式:

    定义:一个类只返回一个实例,一旦创建再次调用就直接返回
    使用场景:比如自定义弹窗,无论你程序中多少调用,都只应创建一个弹窗对象

    class CreateUser {
        constructor(name) {
            this.name = name;
            this.getName();
        }
        getName() {
            return this.name;
        }
    };
    
    const ProxyMode = (() => {
        let instance = null;
        return (name) => {
            if(!instance) {
                instance = new CreateUser(name);
            }
            return instance;
        }
    })();
    
    let a = ProxyMode('vn');
    let b = ProxyMode('lb');
    
    console.log(a, b);   // vn  vn    单例模式只会创建一次实例
    

    策略模式:

    定义:定义一个策略类只专注与各方法算法实现,定义一个接口调用这些方法
    特点:代码优雅,可读性高

    // 策略类
    const levelObj = {
        "A": money => money * 4,
        "B": money => money * 3,
        "C": money => money * 2
    }
    
    // 环境类  封装调用接口
    const getMoney = (level, money) => levelObj[level](money);
    
    console.log(getMoney('A', 200))   // 800
    

    代理模式:

    定义:为一个对象提供一个代用品或占位符,以便控制对它的访问
    使用场景:比如图片懒加载,先缓存动态 loading,必要时传入 src

    const imgFunc = (() => {
        let imgNode = document.createElement('img');
        document.body.appendChild(imgNode);
        return {
            setSrc: (src) => {
                imgNode.src = src;
            }
        }
    })();
    
    const ProxyImg = (() => {
        let img = new Image();
        img.onload = () => {
            let node = document.getElementsByTagName('img')
            imgFunc.setSrc(img.src);
        }
        return {
            setSrc: (src) => {
                imgFunc.setSrc('../C3photo/jacky/1.jpg');
                img.src = src;
            }
        }
    })();
    
    ProxyImg.setSrc('../C3photo/jacky/2.jpg');
    

    装饰者模式:

    定义:装饰者模式能够在不改变对象自身的基础上,在运行程序期间给对象动态地添加职责。
    使用场景:类似于拦截器,添加对象的前置和后置事件等。

    Function.prototype.before = function(beforefn) {
        let _self = this;                          //保存原函数引用
        return function(){                         //返回包含了原函数和新函数的 '代理函数'
            beforefn.apply(this, arguments);       //执行新函数,修正this
            return _self.apply(this, arguments);   //执行原函数
        }
    }
    Function.prototype.after = function(afterfn) {
        let _self = this;
        return function(){
            let ret = _self.apply(this, arguments);
            afterfn.apply(this, arguments);
            return ret;
        }
    }
    let func = function() {
        console.log('2');
    }
    //func1和func3为挂载函数
    let func1 = function() {
        console.log('1');
    }
    let func3 = function() {
        console.log('3');
    }
    
    func = func.before(func1).after(func3);
    func();   // 1  2  3
    

    发布订阅模式:

    定义:订阅者(Subscriber)把自己想订阅的事件注册(Subscribe)到调度中心(Event Channel),当发布者(Publisher)发布该事件(Publish Event)到调度中心,也就是该事件触发时,由调度中心统一调度(Fire Event)订阅者注册到调度中心的处理代码。
    使用场景:微信公众号的订阅

    let eventEmitter = {
        list: {},                        // 缓存列表(调度中心)
    
        on(event, fn) {              // 订阅
            let _this = this;
            _this.list[event] = _this.list[event] || [];    
            _this.list[event].push(fn);
            return _this;
        },
    
        emit() {               // 发布
            let _this = this;
            let event = [].shift.call(arguments),     // shift 会改变原数组,因此 arguments 只剩下第二个参数
            fns = _this.list[event];
            if(fns && fns.length) {
                fns.forEach(fn => fn.apply(_this, arguments));
            }
            return _this;
        },
    
        off(event, fn) {                                 // 取消订阅
            let _this = this;
            let fns = _this.list[event];
            if(!fns) return false;          // 如果缓存列表中没有相应的 fn,返回false
            if(!fn) {
                // 如果没有传 fn 的话,就会将 event 值对应缓存列表中的 fn 都清空
                fns.length = 0;
            } else {
                // 若有 fn,遍历缓存列表,看看传入的 fn 与哪个函数相同,如果相同就直接从缓存列表中删掉即可
                for (let i = 0; i < fns.length; i++) {
                    if (fns[i] === fn || fns[i].fn === fn) {
                        fns.splice(i, 1);
                        break;
                    }
                }
            }
        }
    };
    
    const user1 = (content) => {
        console.log('用户1订阅了:', content);
    }
    
    const user2 = (content) => {
        console.log('用户2订阅了:', content);
    }
    
    const user3 = (content) => {
        console.log('用户3订阅了:', content);
    }
    
    // 订阅
    eventEmitter.on('article1', user1);
    eventEmitter.on('article1', user2);
    eventEmitter.on('article2', user3);
    
    eventEmitter.emit('article1', 'Javascript 发布-订阅模式');
    eventEmitter.emit('article2', 'Javascript 观察者模式');
    
    eventEmitter.off('article1', user1);
    eventEmitter.emit('article1', 'Javascript 发布-订阅模式');
    
    
    //用户1订阅了: Javascript 发布-订阅模式
    //用户2订阅了: Javascript 发布-订阅模式
    //用户3订阅了: Javascript 观察者模式
    //用户2订阅了: Javascript 发布-订阅模式
    

    总结:

    其实当你学了这么久的前端,做了这么久的项目,回来看看设计模式会发现它的思想其实挺有意思的,当你试着在工作中写出这些设计模式,你的技术又会上一层台阶啦。

    相关文章

      网友评论

          本文标题:JS - 开发中的设计模式

          本文链接:https://www.haomeiwen.com/subject/ckiixctx.html