前端面试——设计模式之单例模式

单例模式

核心:确保只有一个实例,并提供全局访问 (注意:全局变量不是单例模式)

实现:用一个变量标志当前是否已经为某个类创建过对象,如果是,则在下次创建类实例的时候,直接返回之前创建的对象(用闭包缓存数据),若不是则通过new创建一个新的对象

应用场景:登录框

透明的单例模式

需求:实现一个透明的单例类,用户从这个类中创建对象的时候,可以像使用其他任何普通类一样。

为了把标志变量instance封装起来,此处使用自执行的匿名函数和闭包

const CreateDiv=(function(){
    let instance;//标记变量

    const CreateDiv=function(html){
        if(instance){//若已创建,直接返回之前创建的对象
            return instance;
        }
        //否则重新创建
        this.html=html;
        this.init();
        return instance=this;
    }

    CreateDiv.prototype.init=function(){
        const div=document.createElement('div');
        div.innerHTML=this.html;
        document.body.appendChild(div);
    }

    return CreateDiv;
})();

const a=new CreateDiv('amethyst');
const b=new CreateDiv('lanlan');

console.log(a===b);//true,实际上只有一个innerHTML为amethyst的对象

缺点:

  • CreateDiv函数实际上负责了两件事(违背了单一职责原则):
    • 创建对象和执行初始化方法init()
    • 保证只有一个对象
  • 若要将单例类变成一个可以产生多个实例的普通类,需要改写CreateDiv构造函数,把控制唯一对象那一段代码去掉,操作繁琐且效率低
用代理实现单例模式

通过代理的方式解决上述问题

实现思路:

  • 先在CreateDiv中,把控制唯一对象的部分移出去,使CreateDiv成为一个普通的创建div的类

  • const CreateDiv = function (html) {
        this.html = html;
        this.init();
    }
    
    CreateDiv.prototype.init = function () {
        const div = document.createElement('div');
        div.innerHTML = this.html;
        document.body.appendChild(div);
    }
    
  • 引入代理类proxySingletonCreateDiv

  • const proxySingletonCreateDiv = (function () {
        let instance;
        return function (html) {
            if (instance) return instance;
            return instance = new CreateDiv(html);
        }
    })();
    
  • 测试

  • //普通类
    const a = new CreateDiv('amethyst');
    const b = new CreateDiv('lanlan');
    
    //单例类
    const a1 = new proxySingletonCreateDiv('amethyst');
    const b1 = new proxySingletonCreateDiv('lanlan');
    
    console.log(a === b);//false
    console.log(a1 === b1);//true
    
惰性单例
  • 指的是在需要的时候才创建对象实例

应用场景:网页登录框

实现:也是用一个变量标记是否已经创建过登录框

通用的惰性单例:

  • 把管理单例的逻辑抽离出来,封装在getSingle函数内部,创建对象的方法被当做参数动态传入getSingle函数

  • const getSingle=function(fn){
        let instance;
        return function(){
            if(instance) return instance;
            return instance=fn.apply(this,arguments);
        }
    }
    
  • 调用getSingle的时候,将用于创建登录框的函数fn以参数形式传入(还可以传入创建iframe等其他的函数),之后getSingle返回一个新的函数,变量instance用于保存fn的计算结果,并且instance处在闭包中,永远不会被销毁

  • 若instance已有值,说明已经创建过对象,直接返回已创建的

  • 创建登录框

  • //创建登录框的函数
    const createLoginLayer = function () {
        const div = document.createElement('div');
        div.innerHTML = '我是登录框';
        div.style.display = 'none';
        document.body.appendChild(div);
        return div;
    }
    
    //将createLoginLayer包装成创建单例的函数
    const createSingleLoginLayer = getSingle(createLoginLayer);
    
    //在需要使用的时候调用createSingleLoginLayer获取单例
    const btnObj = document.querySelector('#btn');
    btnObj.addEventListener('click', function () {
        const login = createSingleLoginLayer();
        login.style.display = 'block';
    });
    
  • 创建iframe

  • //创建iframe
    const createSingleIframe = getSingle(function () {
        const iframe = document.createElement('iframe');
        document.body.appendChild(iframe);
        return iframe;
    });
    
    document.querySelector('#btn1').addEventListener('click',function(){
        const iframe=createSingleIframe();
        iframe.src='https://www.baidu.com/';
    });
    
    

单例模式是一种简单且非常实用的模式,特别是惰性单例技术,在合适的时候才创建对象,并且只创建唯一的一个,并且创建对象和管理单例的职责被分布在两个不同的方法中,结合起来即可完成创建唯一实例对象的功能

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值