JavaScript常见设计模式

设计模式的定义:在面向对象软件设计过程中针对特定问题的简洁而优雅的解决方案

1、单例模式

单例模式的定义:保证一个类仅有一个实例,并提供一个访问它的全局访问点。实现的方法为先判断实例存在与否,如果存在则直接返回,如果不存在就创建了再返回,这就确保了一个类只有一个实例对象。

适用场景:一个单一对象。比如:弹窗,无论点击多少次,弹窗只应该被创建一次。

通过闭包代理实现单例模式,共用一份instance

class CreateUser {
    constructor(name) {
        this.name = name;
        this.getName();
    }
    getName() {
         return this.name;
    }
}
// 代理实现单例模式
var ProxyMode = (function() {
    var instance = null;//闭包保存instance变量
    return function(name) {
        if(!instance) {//不存在就创建
            instance = new CreateUser(name);
        }
        return instance;//存在则直接返回
    }
})();
// 测试单体模式的实例
var a = new ProxyMode("aaa");
var b = new ProxyMode("bbb");
// 因为单体模式是只实例化一次,所以下面的实例是相等的
console.log(a === b);    //true

2、策略模式

策略模式的定义:定义一系列的算法,把他们一个个封装起来,并且使他们可以相互替换。策略模式主要解决的是if-else逻辑复杂而不可维护的情况。

一个基于策略模式的程序至少由两部分组成。第一个部分是一组策略类(可变),策略类封装了具体的算法,并负责具体的计算过程。第二个部分是环境类Context(不变),Context接受客户的请求,随后将请求委托给某一个策略类。

/*策略类*/
var levelOBJ = {
    "A": function(money) {
        return money * 4;
    },
    "B" : function(money) {
        return money * 3;
    },
    "C" : function(money) {
        return money * 2;
    } 
};
/*环境类*/
var calculateBouns =function(level,money) {
    return levelOBJ[level](money);
};
console.log(calculateBouns('A',10000)); // 40000

3、代理模式

记录:小明追女生A

  • 非代理模式:小明=花=>女生A
  • 代理模式:小明=花=>让女生A的好友B帮忙=花=>女生A

代理模式的定义:为一个对象提供一个代用品或占位符,以便控制对它的访问。

常用的虚拟代理形式:某一个花销很大的操作,可以通过虚拟代理的方式延迟到这种需要它的时候才去创建(例:使用虚拟代理实现图片懒加载)

图片懒加载的方式:先通过一张loading图占位,然后通过异步的方式加载图片,等图片加载好了再把完成的图片加载到img标签里面。

var imgFunc = (function() {
    var imgNode = document.createElement('img');
    document.body.appendChild(imgNode);//创建img节点并挂载到文档
    return {//设置src
        setSrc: function(src) {
            imgNode.src = src;
        }
    }
})();
var proxyImage = (function() {
    var img = new Image();
    img.onload = function() {//当所有资源和图片加载完后回调onload
        imgFunc.setSrc(this.src);
    }
    return {
        setSrc: function(src) {
            imgFunc.setSrc('./loading,gif');//图片预加载loading图
            img.src = src;//保存src到img.src,img加载完src后回调时用this.src
        }
    }
})();
proxyImage.setSrc('./pic.png');

4、中介者模式

中介者模式的定义:通过一个中介者对象,其他所有的相关对象都通过该中介者对象来通信,而不是相互引用,当其中的一个对象发生改变时,只需要通知中介者对象即可。

例如:现实生活中,航线上的飞机只需要和机场的塔台通信就能确定航线和飞行状态,而不需要和所有飞机通信。同时塔台作为中介者,知道每架飞机的飞行状态,所以可以安排所有飞机的起降和航线安排。

中介者模式适用的场景:例如购物车需求,存在商品选择表单、颜色选择表单、购买数量表单等等,都会触发change事件,那么可以通过中介者来转发处理这些事件,实现各个事件间的解耦,仅仅维护中介者对象即可。

var goods = {   //手机库存
    'red|32G': 3,
    'red|64G': 1,
    'blue|32G': 7,
    'blue|32G': 6,
};
//中介者
var mediator = (function() {
    var colorSelect = document.getElementById('colorSelect');
    var memorySelect = document.getElementById('memorySelect');
    var numSelect = document.getElementById('numSelect');
    return {
        changed: function(obj) {
            switch(obj){
                case colorSelect:
                    //TODO
                    break;
                case memorySelect:
                    //TODO
                    break;
                case numSelect:
                    //TODO
                    break;
            }
        }
    }
})();
colorSelect.onchange = function() {
    mediator.changed(this);
};
memorySelect.onchange = function() {
    mediator.changed(this);
};
numSelect.onchange = function() {
    mediator.changed(this);
};

5、装饰者模式

装饰者模式是指,在不改变对象自身代码的前提下,新增功能的模式。

如下代码所示,创建3个飞机对象。plane可以发射普通子弹plane2不仅可以发射普通子弹,还可以发射导弹plane3不仅可以发射普通子弹导弹,还可以发射炮弹

创建构造函数Plane2时,不改变plane的功能,只增加功能,将Plane的实例挂载到自身属性上,并在fire方法下,调用Plane的方法,并增加一个功能:发射导弹。Plane3同理

const strategy = {
    s(base) {
        return base * 5;
    },
    a(base) {
        return base * 4;
    },
    b(base) {
        return base * 3;
    },
    c(base) {
        return base * 2;
    },
    d(base) {
        return base * 1;
    },
};

const getBonus = (base, grade) => strategy[grade](base);

const res1 = getBonus(1000, 's'); // 5000
const res2 = getBonus(1500, 'a'); // 6000

6、发布订阅模式

发布订阅模式,也叫观察者模式。它定义对象间的一种一对多的关系,当一个对象的状态发生改变时,所有依赖于它的对象都将得到通知。

实际上,一个普通的事件绑定,也是一种发布订阅模式。如下代码所示,发布者为用户,监听者为绑定的事件。

document.addEventListener('click', e => {
    console.log(e);
});

如下代码所示,模拟明星发布消息,粉丝收到消息的场景。在这个场景中,明星star发布消息后,每个粉丝都能收到消息,并作出不同的反应。

发布者star监听者为每个粉丝。

class Star {
    constructor() {
        // 粉丝队列
        this.followers = [];
    }
    follow(obj) {
        // 将obj加入粉丝队列
        this.followers.push(obj);
    }
    publish() {
        // 发布消息,通知粉丝
        this.followers.forEach(item => {
            item.fn(item.name);
        });
    }
}

const star = new Star();

// 三个用户关注star
star.follow({
    name: 'Jack',
    fn(name) {
        console.log(`${name}看到消息!`);
    },
});
star.follow({
    name: 'Jone',
    fn(name) {
        console.log(`${name}看到消息,并点赞!`);
    },
});
star.follow({
    name: 'Tom',
    fn(name) {
        console.log(`${name}看到消息,并评论!`);
    },
});

// star发布消息
star.publish();
// Jack看到消息!
// Jone看到消息,并点赞!
// Tom看到消息,并评论!
阅读剩余
THE END