JS - 开发中的设计模式

导读:

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

单例模式:

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

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 发布-订阅模式

总结:

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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,456评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,370评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,337评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,583评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,596评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,572评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,936评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,595评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,850评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,601评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,685评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,371评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,951评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,934评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,167评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,636评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,411评论 2 342

推荐阅读更多精彩内容

  • javascript设计模式与开发实践 设计模式 每个设计模式我们需要从三点问题入手: 定义 作用 用法与实现 单...
    穿牛仔裤的蚊子阅读 4,032评论 0 13
  • 工厂模式类似于现实生活中的工厂可以产生大量相似的商品,去做同样的事情,实现同样的效果;这时候需要使用工厂模式。简单...
    舟渔行舟阅读 7,716评论 2 17
  • 工厂模式 单体模式 模块模式 代理模式 职责链模式 命令模式 模板方法模式 策略模式 发布-订阅模式 中介者模式 ...
    HelloJames阅读 1,004评论 0 6
  • 产后恢复到底是恢复什么? 产后恢复,对于新妈妈来说,和宝宝的健康成长一样重要。 产后恢复的本质不仅是妈咪们身材的婀...
    妈妈修复阅读 171评论 0 0