乐趣区

关于javascript:javascript设计模式单例模式

从明天开始,我将间断更新 javascript 的设计模式,材料起源次要是 https://www.patterns.dev/ 这里涵盖了所有的设计模式,次要内容来自对这个网站的翻译加上本人的了解,也是自我学习的过程

前沿

设计模式是软件开发的根本组成部分,因为在大型的软件设计过程中,常常会呈现一些设计模式,它能够用来优化咱们一些代码逻辑和解决形式
过来的一段时间中,web 整个开发体系产生了迅猛的变动,尽管一些设计模式可能曾经没有了对应的价值,然而也有其中的一些设计模式曾经倒退到了解决一些古代问题
react 置信大家都比拟相熟,在最近一段时间内取得了微小的关注,哦不对,始终以来 react 都备受关注,从 npm 包的下载量就能很显著的看进去,react 始终稳居几大框架榜首

也因为 react 的风行,传统的设计模式曾经被批改优化,并且创立了新的设计模式,以便于在古代 web 开发中提供对应的价值,比方 react 的 hook 个性,能够代替很多传统设计模式,所以才有了这个专题,心愿可能通过一起学习将设计模式的实现、益处、缺点以及面试通通拿下

单例模式

单例示意的是能够实例化一次的类,并且能够全局拜访。这个繁多的实例能够在咱们的应用程序中共享,所以单例模式非常适合管理应用程序中的全局状态
咱们看一下单例到底是一个什么样的内容,咱们能够构建一个 Counter 类,它有以下办法

  • 返回实例(getInstance)
  • 返回计数器以后的值(getCount)
  • count 值加一(increment)
  • count 值减一(decrement)

    let counter = 0
    
    class Counter {getInstance() {return this}
    
    getCount() {return counter}
    
    increment() {return ++counter}
    
    decrement() {return --counter}
    }

    尽管上述的性能都实现了,然而这个类实际上是不符合标准的,标准规定的是这个类只能实例化一次,然而下面的代码咱们能够创立很多 Counter 实例

    const counter1 = new Counter()
    const counter2 = new Counter()
    
    console.log(counter1.getInstance() == counter2.getInstance()) // false

    通过两次实例化的形式之后,获取到的实例并不相等,两个实例只是不同实例的援用

    只能一个实例

    确保咱们只能创立一个实例的方法是创立一个名为 instance 的变量,在构造函数中,咱们能够在创立实例的时候将实例设置为对实例的援用,而后查看 instantce 变量是否曾经有值来避免反复实例化,如果曾经实例化,则抛出谬误让用户晓得曾经存在了实例

    let counter = 0
    let instance = null
    
    class Counter {constructor() {if(instance) {throw new Error("只能有一个 Counter 实例")
      }
    }
    getInstance() {return this}
    
    getCount() {return counter}
    
    increment() {return ++counter}
    
    decrement() {return --counter}
    }
    
    const counter1 = new Counter();
    const counter2 = new Counter();
    // Error: You can only create one instance!

    这样就不能创立多个实例了

    Object.freeze

    这个时候咱们须要导出咱们的实例,然而在导出之前,咱们应该应用 Object.freeze 办法确保初始化实例不会被批改,升高应用危险

    const singletonCounter = Object.freeze(new Counter());
    export default singletonCounter;

    这样咱们就将 singletonCounter 导出了,咱们能够在任意 JavaScript 文件应用 singletonCounter
    在不同的文件调用,数据都是共享的,都可能扭转 counter 值,并且可能读取到最新的值

    优缺点

    将实例化限度为一个实例会节俭大量内存空间,不必每次都给新的实例分配内存,在整个利用中这个实例都可能被援用,然而单例模式被认为是一种反模式,应该在 JavaScript 中防止
    其它的编程语言中,比方 java 或者 c ++,不可能跟 javascript 一样间接创建对象,在面向对象的编程语言中,须要创立一个类,这个类会创立一个对象,该创立的对象具备类实例的值,就像 javascript 中的实例值一样
    其实下面的一系列的操作,齐全能够应用一个简略的惯例对象来代替,比方

    let count = 0;
    
    const counter = {increment() {return ++count;},
    decrement() {return --count;}
    };
    
    Object.freeze(counter);
    export {counter};

    这样能够实现 Counter 类等价的成果
    还有一些暗藏危险,比方咱们在一个单例中引入了另一个单例,这里咱们创立了一个 superCounter 的实例,其中引入了 Counter 实例,在别的文件中如果引入 Counter 实例可能就会造成危险,一旦调用了 super Counter,那 Counter 也会被扭转

    import Counter from "./counter";
    
    export default class SuperCounter {constructor() {this.count = 0;}
    
    increment() {Counter.increment();
      return (this.count += 100);
    }
    
    decrement() {Counter.decrement();
      return (this.count -= 100);
    }
    }

    总结

    一个单例实例应该可能在整个利用中被援用,领有全局行为也会被感觉是一个蹩脚的设计,因为你能够随便更改它,然而你并不知道你到底在哪里更改了它
    在 react 中,常常通过 redux 或者 react context 等状态管理工具来进行全局状态治理,而不是应用单例模式,即便它们看起来这么像单例模式,这些工具提供了只读状态而不是单例的可变状态,应用 redux 时,只有纯函数的 reducer 能够在组建外部通过调度程序发送 action 进行状态更新,这些工具也存在全局状态的毛病,然而它能够让全局状态依照咱们制订的规定或者程序产生扭转

退出移动版