JS 设计模式

面向对象思想:封装、继承、多态

#构造器模式

无非就是继承来实现的啦 - -!

构造器模式案例省略...

思考:为什么ES5的继承要写在 prototype 中,而不是直接写在构造方法里?

答:写在构造器中,无法做到数据共享(会造成资源浪费)

所以 prototype 中会存放需要共享数据的方法和属性(基本上都是方法)

而构造器中会存放不需要共享的属性和方法

#模块化模式

模块化嘛:AUCommonJS/Module 都是模块化,对象、闭包也是模块化

在JS中,模块化模式其实是模拟了"类"的概念。好处是有私密空间,不会造成全局污染。

虽然JS没有私有属性。但闭包能很好的实现私有属性的概念

简单的模块化模式:(闭包 - -.!)

    var module = (function () {

        var num = 0;

        return {

            getNum: function () {

                return num;

            },

            addNum: function () {

                return num++;

            }

        };

    })();

    console.log(module)


    module.addNum()

    console.log(module.getNum())

#单例模式

单例:就是限制一个类只能有一个实例化对象

使用场景案例:“警告/确认/提示弹窗”(只能存在一个的情况)

最简单的单例::闭包 + Flag 来实现

    const mySingleton = (function () {

        let _instance;

        return function () {

            if (!_instance) {

                _instance = {

                    x: 1,

                    setX: (arg) => {

                        _instance.x = arg;

                    }

                };

            }

            return _instance;

        }


    })()

    const instanceA = mySingleton();

    const instanceB = mySingleton();

    console.log(instanceA === instanceB);

封装一下:(PS: ES5的new如果有return使用return的值)

    const Singleton = (function() {

        var _instance;

        return function(obj) {

            return _instance || (_instance = obj);

        }

    })();

    var a = new Singleton({x: 1});

    var b = new Singleton({y: 2});

    console.log(a === b);

#观察者模式

由观察者和观察者组成。通过观察者调用被观察者的实例。

观察者模式:观察者对象和被观察者对象 之间的订阅和触发事件

使用场景案例:“Vue 双向绑定实现”

简单的观察者模式: (仿 Vue 实现)

    // 观察者

    class Dep {

        constructor() {

            this.subs = []

        }


        addSub(sub) {

            this.subs.push(sub)

        }


        depend() {

            if (Dep.target) {

                Dep.target.addDep(this);

            }

        }


        notify() {

            this.subs.forEach(sub => sub.update())

        }

    }


    // 被观察者

    class Watcher {

        constructor(vm, expOrFn) {

            this.vm = vm;

            this.getter = expOrFn;

            this.value;

        }

        get() {

            Dep.target = this;


            var vm = this.vm;

            var value = this.getter.call(vm, vm);

            return value;

        }

        evaluate() {

            this.value = this.get();

        }

        addDep(dep) {

            dep.addSub(this);

        }


        update() {

            console.log('更新, value:', this.value)

        }

    }


    // 观察者实例

    var dep = new Dep();


    //  被观察者实例

    var watcher = new Watcher({x: 1}, (val) => val);

    watcher.evaluate();


    // 观察者监听被观察对象

    dep.depend()


    dep.notify()

#发布/订阅者模式

由订阅者 Subscriber 和发布者 Publisher 组成。

发布/订阅者模式:是观察者模式的变体,比观察者模式多了一个调度中心

发布者发布信息到调度中心

调度中心和订阅者直接完成订阅和触发事件事件

使用场景案例:“DOM 的 addEventListener 事件”

一个简单的发布/订阅者模式实现:(仿 EventBus 实现)

    // EventTarget 就是一个调度中心

    class EventTarget {

        constructor() {

            this.dep = {}

        }


        on(key, fn) {

            this.dep[key] = fn;

        }


        emit(key) {

            typeof this.dep[key] === 'function' ? this.dep[key]() : ''

        }

    }


    let eventTarget = new EventTarget()


    eventTarget.on('click', function() {console.log(1)})

    eventTarget.emit('click')

#中介者模式

中介:撮合多个卖家 和 多个买家

    class Saler {

        constructor(name, cost) {

            this.name = name;

            this.cost = cost;

        }


        send() {

            console.log(`${cost}元出售${name}`)

        }

    }


    class Agency {

        constructor() {

            this.cargos = []

        }


        register(saler) {

            this.cargos.push(saler);

        }


        query(name) {

            const matchCargos = this.cargos.filter(cargo => cargo && cargo.name === name);

            if (matchCargos.length) {

                console.log(`查询到正在出售的商品:${JSON.stringify(matchCargos)}`)

            } else {

                console.log(`没有${name}在出售`);

            }

        }

    }


    let agency = new Agency();


    agency.query('cart');


    const cartA = new Saler('cart', '100');

    const cartB = new Saler('cart', '300');

    const house = new Saler('house', '500');

    agency.register(cartA);

    agency.register(cartB);

    agency.register(house);


    agency.query('cart');

    agency.query('house');

    agency.query('ABC');

#命令模式

为方法的调用进行解耦

    const command = {

        buy(name, cost) {

            console.log(`购买${name}消费了${cost}元`)

        },

        sale(name, cost) {

            console.log(`出售了${name}赚得${cost}元`)

        },

        say(name, cost) {

            console.log(`这里${cost}元可以买到${name}`)

        },

        execute(fnName) {

            const fn= this[fnName];

            (typeof fn === 'function') && fn.apply(this, [].slice.call(arguments, 1))

        }

    }


    command.execute('buy', 'VIP', '200');

    command.execute('sale', '节操', '998');

    command.execute('say', 'VIP', '123');

#策略模式

策略模式最大的好处是:减少if-else的使用,同时增加代码可读性

简单的年终奖计算。(策略模式放在必填项/规则验证会很便捷)

    // 策略模式

    const bonus = {

        A: function(base) {

            return base * 4;

        },

        B: function(base) {

            return base * 3;

        },

        C: function(base) {

            return base * 2;

        },

        D: function(base) {

            return base;

        }

    }

    const level = "B";

    const base = "1008611";

    const yearBouns = bonus[level](base);

    console.log(yearBouns)

#工厂模式

一个工厂(类) 能生产各种零件(实例)

#简单的工厂模式

通过一个类获取不同类的实例

    class Cat {}

    class Dog {}

    class Pig {}


    function Factory(type, args) {

        switch (type){

            case 'cat':

                return new Cat(args);

                break;

            case 'dog':

                return new Dog(args);

                break;

            default:

                return new Pig(args);

                break;

        }

    }


    const cat = new Factory('cat', {name: 'cat'});

    const dog = new Factory('dog', {name: 'dog'});

    const pig = new Factory('pig', {name: 'pig'});


    console.log(cat, dog, pig)

#抽象工厂模式

通过继承抽象的类(含有未实现的方法)、结合简单工厂模式,生成抽象工厂

抽象工厂的好处:通用方法写在工厂函数中,不需要重复实现,不同个性化代码在子类中实现

实现:省略...

#复杂工厂模式

允许工厂产生的不同零件一起工作:

    class Wheel {

        turn() {

            console.log('轮子开始转动啦');

        }

    }


    class Oil {

        warn() {

            console.log('汽油不足')

        }

    }


    class Cart {

        constructor() {

            this.cart = {}

        }


        getPart(name, args) {

            return this.cart[name] ? new this.cart[name](args) : null;

        }


        setPart(name, Part) {

            this.cart[name] = Part;

        }

    }


    const cart = new Cart();


    cart.setPart('wheel', Wheel)

    cart.setPart('oil', Oil)


    const wheel = cart.getPart('wheel', {name: '轮子A'});

    const oil = cart.getPart('oil', {name: '汽油A'});


    wheel.turn();

    oil.warn();

#修饰器模式

修饰:不改变原有对象,在其基础上进行拓展

基本上每天都在用的设计模式...

简单的修饰模式实现:

    const after = function(fn, afterFn) {

        return function() {

            fn.apply(this, arguments)

            afterFn.apply(this, arguments)

        }

    }

    const myAfter = after(after(fn1, fn2), fn3)

    myAfter()

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

推荐阅读更多精彩内容