设计模式

131 阅读4分钟

和Java类似,JS中也有自己的设计模式,我学习的较为常用的就是:

单例模式、观察者模式、代理模式

单例模式

所谓单例模式,顾名思义就是只需创建一个全局对象,单例模式也是软件设计中较为简单但是应用最为频繁的一种模式。它的目的是保证一个类仅有一个实例,并提供一个访问它的全局访问点。他所要解决的问题是,一个全局使用的类频繁地创建与销毁。下面有几种常用的实现方式:

  • 对象字面量

  • 构造函数内部判断

  • 闭包实现

对象字面量

let single={
    name:"tom"
    age:20
}
let obj1=single         //浅拷贝式
let obj2=single
console.log(obj1===obj2)//true

构造函数内部判断

function single(){
    if(single.protect!==undefined){
        return single.protect       //这一步判断的返回值就使得每次创建的对象都是一样的,实现单例
    }
    this.name="tom"
    single.protect=this
}
let obj1=new single()
let obj2=new single()
console.log(obj1)       //single {name:"tom"}
console.log(obj2)       //single {name:"tom"}
console.log(obj1===obj2)//true

闭包实现

let Single = (function(){
        let singleObj;
        function SingleObj(){
            this.name = 'tom';
            //对应一个构造函数,this指向将要创建的对象,这里所有将要创建的对象都叫'tom'
        }
        singleObj = new SingleObj()
        return function(){
            return singleObj;       //这里返回的singleObj就是上面构造函数新创建的
        }
    })();
    let s1 = Single();
    let s2 = Single();
    console.log(s1)         //singleObj {name:"tom"} 
    console.log(s2)        //singleObj {name:"tom"} 
    console.log(s1 === s2)// true

观察者模式(发布-订阅)

定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。现实中的例子就是,你关注的微信公众号,在更新消息时,会给每一个关注的用户推送更新的消息

观察者模式

需要用到两个参与者,发布者和订阅者

发布者对应的功能:

  • 添加订阅者
  • 删除订阅者
  • 设置消息
  • 发布消息(消息是一个私有属性,订阅者只能被动等待接收)

订阅者对应的功能:

  • 接收更新的消息(被动)
 // 发布者
    class Publisher{
        constructor(){
            this.observers = [];    // 订阅者名单
            this.info = '';         // 准备发布的消息,私有属性,外部不能直接访问
        }
        // 设置消息
        setInfo(val){
            this.info = val;
            return this;
        }
        // 获取消息
        getInfo(){
            return this.info;
        }
        // 添加订阅者
        addObserver(obr){
            let flag = true;                        // 默认状态:允许添加
            for( let key in this.observers ){       // 遍历观察者名单
                if(obr === this.observers[key]){    // 当以前的订阅者名单中已经有了当前对象
                    flag = false;                   // 设置状态不允许添加
                }
            }
            if( flag ){                             // 当状态为true才会被添加
                this.observers.push(obr);
            }
            return this                             // 返回发布者
        }
        // 删除订阅者
        removeObserver(obr){
            for( let index in this.observers ){     // 遍历订阅者名单
                if(this.observers[index] === obr){  // 找出订阅者名单中的对象,删除
                    this.observers.splice(index,1);
                }
            }
            return this                              // 返回发布者
        }
        // 发布消息
        publish(){
            for( let obr of this.observers ){       // 遍历订阅者
                obr.updateInfo(this.getInfo());     //订阅者获取更新消息
            }
        }
    }

// 订阅者
    class Observer{
        constructor(name){
            this.name = name;
        }
        updateInfo(info){                           // 被动接收消息。(更新消息)
            console.log(`我是${this.name},我现在接收的消息是:${info}`);
        }
    }

下面创建相应的对象并调用它们

// 创建订阅者
    var ob1 = new Observer('tom');
    var ob2 = new Observer('jack');
    var ob3 = new Observer('alice');
    // 创建发布者
    var p = new Publisher();
    // 添加订阅者
    p.addObserver(ob1);
    p.addObserver(ob2);
    p.addObserver(ob3);

    // 设置一条消息并发布
    p.setInfo('这是第一条消息,发布者开张啦!!!').publish();
    // 设置一条消息,但未发布
    p.setInfo('第二条消息:大家以后想要看独家报道请冲会员啦!!!');
    
    可以看出上面创建都是重复的操作,比较累赘,一个个手动添加也是重复的操作,会让人不满意,
    这就是后期需要进行优化的地方

利用观察者模式,实现数据及时响应,写一个小案例:创建一个input框,在里面输入内容并且及时在页面中显示出来。

<input type="text" id="input">
<div id="output"></div>
function $(ele){document.querySelector(ele)}
//发布者
class publisher{
    constructor(pub){
        this.observes=[]
        this.info=''
        this.pub=pub
    }
    //设置消息
    setInfo(){
        this.info=this.pub.value
        return this
    }
    //获取消息
    getInfo(){
        return this.info
    }
    //添加订阅者
    addObserver(obr){
        let flag=true
        for(let index in this.abserves){
            if(this.abserves[index]===obr){
                flag=false
            }
        }
        if(flag){
            this.abserves.push(obr)
        }
        return this
    }
    //删除订阅者
    removeObserver(obr){
        for(let index in this.observes){
            if(this.observes[index]===obr){
                this.observes.splice(index,1)
            }
        }
        return this
    }
    //发布消息
    publish(){
        for (let obr of this.observes){
            obr.updateInfo(this.getInfo())
        }
    }
}

//订阅者
class Observer{
        constructor(obj){
            this.obj = obj;
        }
        // 被动接收消息。(更新消息)
        updateInfo(info){
            this.obj.innerText = info;
        }
    }
    
    // 创建订阅者对象
    var ob1 = new Observer($('#output'));
    // 创建发布者对象
    var p = new publisher($('#input'));
    // 添加订阅者
    p.addObserver(ob1);

    p.pub.addEventListener('keyup',function(){      //监听函数(触发器)
        p.setInfo().publish();
    },false);    

代理模式

为其他对象提供一种代理以控制对这个对象的访问,主要是解决直接访问可能带来的问题,在其中增加一个中间层。简单点来说,你想追个女孩子,你不好意喊她出来吃饭,你可以通过叫她的闺蜜,让她闺蜜传话

无代理模式

function Girl(name){
        this.name = name;
        this.receive = function(gift){
            console.log('我是'+this.name+',我收到了礼物:'+gift);
        }
    }
    function Boy(name){
        this.name = name;
        this.send = function(girl,gift){
            console.log('我是'+this.name+',我送给'+girl.name+'礼物:'+gift);
            girl.receive(gift);
        };
    }
    let g = new Girl('翠花');
    let b = new Boy('铁柱');
    // 直接送礼物
    b.send(g,'巧克力');

有代理模式

function Girl(name){
        this.name = name;
        this.receive = function(gift){
            console.log('我是'+this.name+',我收到了礼物:'+gift);
        }
    }
    function Boy(name){
        this.name = name;
        this.send = function(proxy,girl,gift){
            console.log('我是'+this.name+',我要送给'+girl.name+'礼物:'+gift);
            proxy.sendGirl(gift,this,girl);
        };
    }
    function ProxyGirl(name){
        this.name = name;
        this.sendGirl = function(gift,boy,girl){
            console.log(girl.name+'有人送你礼物,他的名字叫做:'+boy.name);
            girl.receive(gift);
        }
    }
    let g = new Girl('翠花');
    let b = new Boy('铁柱');
    let p = new ProxyGirl('红红');
    b.send(p,g,'巧克力');