从明天开始,我将间断更新 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 进行状态更新,这些工具也存在全局状态的毛病,然而它能够让全局状态依照咱们制订的规定或者程序产生扭转