【设计模式系列】三、单例模式

今天继续给大家分享设计模式系列——单例模式。

一般来说,单例就是保证一个类只有一个实例,实现的方法一般是先判断实例存在与否,如果存在直接返回,如果不存在就创建了再返回,这就确保了一个类只有一个实例对象。在JavaScript里,单例作为一个命名空间提供者,从全局命名空间里提供一个唯一的访问点来访问该对象。

单例模式是指在内存中只会创建且仅创建一次对象的设计模式。

在程序中多次使用同一个对象且作用相同时,为了防止频繁的创建对象使得内存飙升,单例模式可以让程序仅在内存中创建一个对象,让所有需要调用的地方都共享这一单例对象。

实现

单例模式可以分为两种类型:

  • 懒汉式:在真正需要使用对象的时候才去创建该单例类对象
  • 饿汉式:在类加载时就已经创建好该单例对象,等待被程序使用

注意:上面两种创建单例对象的方式的构造器都要私有化。

在前端开发中 ,实现单例的方式有很多,最简单的方式就是:使用对象字面量的方式。这样对象里可以包含需要的属性和方法:

let mySingleton = {
    property1"something",
    property2"something else",
    method1function ({
        console.log('hello world');
    }
};

如果以后要扩展该对象,可以添加私有成员和方法。然后使用闭包在其内部封装这些变量和函数声明,只暴露你想暴露的public成员和方法。

样例代码如下:

let mySingleton = function ({

    /* 这里声明私有变量和方法 */
    let privateVariable = 'something private';
    function showPrivate({
        console.log(privateVariable);
    }

    /* 公有变量和方法(可以访问私有变量和方法) */
    return {
        publicMethodfunction ({
            showPrivate();
        },
        publicVar'the public can see this!'
    };
};

let single = mySingleton();
single.publicMethod();  // 输出 'something private'
console.log(single.publicVar); // 输出 'the public can see this!'

上面的代码很不错了,但如果我们想做到只有在使用的时候才初始化,那该如何做呢?

为了节约资源的目的,我们可以另外一个构造函数里来初始化这些代码,如下:

let Singleton = (function ({
    let instantiated;
    function init({
        /*这里定义单例代码*/
        return {
            publicMethodfunction ({
                console.log('hello world');
            },
            publicProperty'test'
        };
    }

    return {
        getInstancefunction ({
            if (!instantiated) {
                instantiated = init();
            }
            return instantiated;
        }
    };
})();

/*调用公有的方法来获取实例:*/
Singleton.getInstance().publicMethod();

应用

单例一般是用在系统间各种模式的通信协调上,下面的代码是一个单例的最佳实践:

let SingletonTester = (function ({
    //参数:传递给单例的一个参数集合
    function Singleton(args{

        //设置args变量为接收的参数或者为空(如果没有提供的话)
        let args = args || {};
        //设置name参数
        this.name = 'SingletonTester';
        //设置pointX的值
        this.pointX = args.pointX || 6//从接收的参数里获取,或者设置为默认值
        //设置pointY的值
        this.pointY = args.pointY || 10;

    }

    //实例容器
    let instance;

    let _static = {
        name'SingletonTester',

        //获取实例的方法
        //返回Singleton的实例
        getInstancefunction (args{
            if (instance === undefined) {
                instance = new Singleton(args);
            }
            return instance;
        }
    };
    return _static;
})();

let singletonTest = SingletonTester.getInstance({ pointX5 });
console.log(singletonTest.pointX); // 输出 5 

接着介绍使用惰性单例模式,实现弹框的demo

const createModal = function (message{
  const box = document.createElement('div')
  box.innerHTML = message
  box.className = 'box'
  document.body.appendChild(box)
  return box
}

const createAlertMessaeg = CreateSingleV1(createModal)

modalAlert.onclick = function (params{
  const alertMessaeg = createAlertMessaeg('独孤九剑yyds')
}

总结

来具体说一下单例模式的优缺点吧

  • 优点:单例模式因为是只有一个实例避免了重复的创建销毁占用内存,经常应用在一个弹框或者对于一个用户配置一个购物车的情况,或者是全局状态管理等类似现在流行的vuex/mobx等
  • 缺点:在动态对象的扩展上比较差

最后

《前端面试题宝典》经过近一年的迭代,现已推出 小程序 和 电脑版刷题网站 (https://fe.ecool.fun/),欢迎大家使用~

同时,我们还推出了面试辅导的增值服务,可以为大家提供 “简历指导” 和 “模拟面试” 服务,感兴趣的同学可以联系小助手(微信号:interview-fe)进行报名。