javascript设计模式及应用场景

前言

这里花几分钟时间来讲解几个设计模式以及它们的应用场景。可以帮助你在平时开发时把设计模式用起来

  • 装饰器模式
  • 迭代器模式
  • 发布订阅模式
  • 工厂模式
  • 单例模式
  • 代理模式
  • 外观模式

废话不多说,直接上demo

装饰器模式
装饰器DEMO
<!--index.js-->
function decoratorHoc(target){
    target.customerName = 'xuqiang';
}

@decoratorHoc
class Person{}

console.log(Person.customerName);
复制代码
装饰器疑问?️
  • decoratorHoc是个函数,为何支持@decoratorHoc?不要把这个当作理所当然。可以试下把代码拷入vscode,然后code runner,发现报了这段错

目前浏览器不支持装饰器模式,如果想要让这段代码跑起来的话,需要依赖babel的一个插件:babel-plugin-transform-decorators-legacy

那么下面三步来把babel环境搞一下

  • 安装全局babel
<!--在当前index.js目录下-->
yarn add babel-cli -g

yarn add babel-plugin-transform-decorators-legacy
复制代码
  • 配置.babelrc
<!--在当前index.js目录下新建.babelrc文件,内容如下-->
{
    plugins: ['transform-decorators-legacy']
}
复制代码
  • 编译index.js
<!--在当前index.js目录下-->
babel index.js --out-file compiled.js
复制代码

这里把index.js编译为了compiled.js。然后code runner compiled.js,发现日志输出了"xuqiang"

至此你就实现了装饰器decoratorHoc

装饰器模式应用场景

装饰器模式可以说是非常方便地扩展了一个对象。那么平时开发中是不是有几类代码跟装饰器模式很相似

  • react的高阶组件
  • dva的@connect

这里就举两个例子,更多场景需要你自己去发现了

迭代器模式

迭代一个对象,就可以称为是迭代器,下面从es6的迭代器来讲解下

for-of遍历json

这是一个es6的自定义迭代器DEMO,讲解如何写一个迭代器来遍历普通对象,因为遍历数组内置已经支持了

let obj = {
    name: 'xuqiang',
    age: 20,
    [Symbol.iterator]: () => {
        let props = Object.keys(obj), i = props.length;
        return {
            next(){
                if(i > 0){
                    i = i - 1;
                    return {
                        value: obj[props[props.length - i - 1]],
                        done: false
                    };
                }else{
                    return {
                        value: '',
                        done: true
                    };
                }
            }
        };
    }
}

for(let prop of obj){
    console.log(prop);
}
复制代码

code runner发现可以正确遍历一个Object了

发布订阅模式
发布订阅的场景

用一个老生常谈的场景先来解释下发布订阅,那就是学生时代的订牛奶,先来整理下需求

  • 有一个工厂,有一个let products = {},有一种属性叫做牛奶。此属性对应一个数组,数组存储每个订奶牛人的数据。
  • 工厂有一个定时器,每天早上8点开始遍历products,进行牛奶派发
  • 有很多消费者,向工厂订牛奶

需求整理清楚了,下面用个DEMO来实现下

// 发布订阅模式
// 发布订阅模式

class Puber{
    constructor(name){
        this.name = name;
        this.products = {};
        this.startInterval();
    }

    startInterval(){
        setInterval(() => {
            for(let prop in this.products){
                if(this.products.hasOwnProperty(prop)){
                    this.emit(prop);
                }
            }
        }, 3000);
    }

    sub(evt, cb){
        if(!this.products[evt]){
            this.products[evt] = [];
        }
        this.products[evt].push(cb);
    }

    emit(evt){
        if(this.products[evt]){
            let cbs = this.products[evt];
            for(let cb of cbs){
                cb && cb();
            }
        }
    }
}

class Suber{
    constructor(name){
        this.name = name;
        this.puber = new Puber('徐强');
    }

    getPuber(){
        console.log(`我是${this.name},我的牛奶配送员是${this.puber.name}`);
    }

    sub(evt, cb){
        this.puber.sub(evt, cb);
    }
}

let customer1 = new Suber('刘思琪');
customer1.getPuber();
customer1.sub('牛奶', () => {
    console.log(`现在时间是${+new Date},牛奶送至未来科技城,收件人:刘思琪`);
});

let customer2 = new Suber('徐大佬');
customer2.getPuber();
customer2.sub('牛奶', () => {
    console.log(`现在时间是${+new Date},牛奶送至空港新城,收件人:徐大佬`);
});
复制代码

发现刘思琪和徐大佬,已经成功订了牛奶,并且每天早上10点钟开始派发牛奶了。当然了,这里没有写取消订牛奶的操作,大家可以自己接着写

工厂模式
class jQuery{
    constructor(name){
        this.name = name;
    }
}

function $(name){
    return new jQuery(name);
}

let obj1 = $('xuqiang');
let obj2 = $('liusiqi');
console.log(obj1);
console.log(obj2);
复制代码
工厂模式总结

这里举例也是用的jquery,可以发现jquery的$函数就是用了工厂模式,那么工厂模式有哪些好处呢?

  • 不需要自己调用new jQuery。直接用$很方便
  • 类似React.createElement,屏蔽了开发者直接使用new VNode,符合开放封闭原则,VNode的实现对开发者不可见
单例模式

光是单例模式的话比较简单,感觉没什么亮点,所以这里结合装饰器模式来写一个单例模式

写一个装饰器模式+单例模式的骚操作
function getInstanceHoc(target){
    target.getInstance = (() => {
        let instance;
        return () => {
            if(!instance){
                instance = new Function(`return new ${target}()`)();
            }
            return instance;
        }
    })();
}

@getInstanceHoc
class Person{
    eat(){
        console.log('i am eating');
    }
}

let obj = Person.getInstance();
console.log(obj);
obj.eat();

let obj1 = Person.getInstance();
console.log(obj === obj1);
复制代码

code runner之后发现,obj和obj1都可以eat了,然后比较obj===obj1,发现是true,说明单例模式已经成功了

代理模式

用es6的Proxy来讲一下代理

class Vue{
    constructor(data){
        let _data =  data;

        return new Proxy(this, {
            get(target, key){
                return _data[key];
            },
            set(target, key, val){
                _data[key] = val;
                return true;
            }
        });
    }
}

let obj = new Vue({
    name: '徐强',
    age: 20
});
console.log(obj.name);
obj.name = '刘思琪';
console.log(obj.name);
复制代码

外观模式

外观模式在jquery中很常见。在我们平时开发中感觉也很好用

function winAlert(title, message, buttons, cb){
    if(cb === undefined){
        cb = buttons;
        buttons = null;
    }

    console.log(title);
    console.log(message);
    console.log(buttons);
    console.log(cb);
}

winAlert('提示', '操作完成', ['确认', '取消'], () => {});

winAlert('提示', '操作完成', () => {});
复制代码

运行结果:

这让我想起目前业务中后端所有接口都是app/htmlGateway.do,只是version,rd等参数区别,从某种角度来说这是符合外观模式的。封闭后端api的内部实现,开放htmlGetway给前端调用

但是目前这种方式有个问题是,对于yapi,rap2等mock平台不太友好。因为api路径都是一致的,会不太好mock,也可能是因为我没有发现解决方案吧,所以觉得不太好mock

ok 这几种设计模式的demo都已经讲完了。有问题可以一起谈论。

转载于:https://juejin.im/post/5d186e75e51d45105d63a553

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值