why

  • 某个类对于所有用户只有一个可用的实例
    • 控制某些共享资源的访问权限
  • 需要严格地控制全局变量

    How

  1. 在类中添加一个私有静态成员变量用于保存单例实例。
  2. 声明一个公有静态构建方法用于获取单例实例。
  3. 在静态方法中实现”延迟初始化”。该方法会在首次被调用时创建一个新对象,并将其存储在静态成员变量中。此后该方法每次被调用时都返回该实例。
  4. 将类的构造函数设为私有。类的静态方法仍能调用构造函数,但是其他对象不能调用。
  5. 检查客户端代码,将对单例的构造函数的调用替换为对其静态构建方法的调用。 ```typescript /**

    • The Singleton class defines the getInstance method that lets clients access
    • the unique singleton instance. */ class Singleton { private static instance: Singleton;

      /**

      • The Singleton’s constructor should always be private to prevent direct
      • construction calls with the new operator. */ private constructor() { }

      /**

      • The static method that controls the access to the singleton instance. *
      • This implementation let you subclass the Singleton class while keeping
      • just one instance of each subclass around. */ public static getInstance(): Singleton { if (!Singleton.instance) {

        1. Singleton.instance = new Singleton();

        }

        return Singleton.instance; }

      /**

      • Finally, any singleton should define some business logic, which can be
      • executed on its instance. */ public someBusinessLogic() { // … } }

/**

  • The client code. */ function clientCode() { const s1 = Singleton.getInstance(); const s2 = Singleton.getInstance();

    if (s1 === s2) {

    1. console.log('Singleton works, both variables contain the same instance.');

    } else {

    1. console.log('Singleton failed, variables contain different instances.');

    } }

clientCode();

  1. <a name="ffT7z"></a>
  2. # 应用场景
  3. <a name="BjB5d"></a>
  4. ## Vuex 中的单例模式
  5. > 一个 Vue 实例只能对应一个 Store
  6. ```javascript
  7. let Vue // 这个Vue的作用和楼上的instance作用一样
  8. ...
  9. export function install (_Vue) {
  10. // 判断传入的Vue实例对象是否已经被install过Vuex插件(是否有了唯一的state)
  11. if (Vue && _Vue === Vue) {
  12. if (process.env.NODE_ENV !== 'production') {
  13. console.error(
  14. '[vuex] already installed. Vue.use(Vuex) should be called only once.'
  15. )
  16. }
  17. return
  18. }
  19. // 若没有,则为这个Vue实例对象install一个唯一的Vuex
  20. Vue = _Vue
  21. // 将Vuex的初始化逻辑写进Vue的钩子函数里
  22. applyMixin(Vue)
  23. }

全局模态框

  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8">
  5. <title>单例模式弹框</title>
  6. </head>
  7. <style>
  8. #modal {
  9. height: 200px;
  10. width: 200px;
  11. line-height: 200px;
  12. position: fixed;
  13. left: 50%;
  14. top: 50%;
  15. transform: translate(-50%, -50%);
  16. border: 1px solid black;
  17. text-align: center;
  18. }
  19. </style>
  20. <body>
  21. <button id='open'>打开弹框</button>
  22. <button id='close'>关闭弹框</button>
  23. </body>
  24. <script>
  25. // 核心逻辑,这里采用了闭包思路来实现单例模式
  26. const Modal = (function() {
  27. let modal = null
  28. return function() {
  29. if(!modal) {
  30. modal = document.createElement('div')
  31. modal.innerHTML = '我是一个全局唯一的Modal'
  32. modal.id = 'modal'
  33. modal.style.display = 'none'
  34. document.body.appendChild(modal)
  35. }
  36. return modal
  37. }
  38. })()
  39. // 点击打开按钮展示模态框
  40. document.getElementById('open').addEventListener('click', function() {
  41. // 未点击则不创建modal实例,避免不必要的内存占用;此处不用 new Modal 的形式调用也可以,和 Storage 同理
  42. const modal = new Modal()
  43. modal.style.display = 'block'
  44. })
  45. // 点击关闭按钮隐藏模态框
  46. document.getElementById('close').addEventListener('click', function() {
  47. const modal = new Modal()
  48. if(modal) {
  49. modal.style.display = 'none'
  50. }
  51. })
  52. </script>
  53. </html>

参考资料

  1. 单例模式
  2. 单例模式——Vuex 的数据管理哲学