乐趣区

关于javascript:JavaScript设计模式及代码实现单例模式

单例模式

1 定义

保障一个类仅有一个实例,并提供一个拜访它的全局拜访点。

2 利用机会

  1. 当一个类的实例被频繁应用,如果反复创立这个实例,会无端耗费资源。比方 dialog 弹窗会被全局重复使用
  2. 业务性能自身决定了全局只能有惟一的实例。比方 redux 治理的数据,只能有惟一的一份

3 利用场景

  1. 对于前端利用的许多根本组件:比方 dialog、message 等等,会被全局频繁应用,就应该保护一个全局惟一的实例,防止反复创立带来不必要的资源耗费。业务组件也同理:比方购物车组件、登录弹窗组件等
  2. 对于一些通用的工具库,常常会应用单例模式。比方咱们通常会创立一个全局惟一的 axios 实例来发动网络申请
  3. 对于 redux、vuex 等状态治理库,都采纳全局惟一的 store 来存储利用状态数据

等等 ……

4 代码实现

4.1 全局变量和命名空间

依据单例模式的定义

// 保护类 A 的惟一实例
class A {}
window.a = new A(); // 或 global.a = new A(); 浏览器用 window

这种办法存在很显著的缺点,因为同一我的项目的所有程序员都能够定义全局的变量 a,很容易造成全局净化。

解决办法是设定一个本人的命名空间来和其他人辨别

// 比方我设定本人的命名空间 JiMing
window.JiMing = {a: new A()
}

如果应用 TypeScript,能够应用关键字 namespace

namespace JiMing {export const a = new A();
}

4.2 惰性单例

上述实现中,咱们间接在全局创立了类 A 的繁多实例,无论其是否被应用,这在某些场景会造成资源节约。有时咱们心愿在用到的时候再创立实例

如下代码利用立刻执行函数和闭包来失去 A 的单例获取函数:getSingletonOfA

class A {}
const getSingletonOfA = (() => {
  let instance;
  return () => {return (instance ??= new A());
  };
})();

只有在调用 getSingletonOfA 才会创立 A 的实例,并且会在闭包中将其贮存在 instance 中,反复调用 getSingletonOfA 会获取雷同的实例

const a1 = getSingletonOfA();
const a2 = getSingletonOfA();
console.log(a1 === a2); // true

上述办法可能满足单例模式,然而不够通用,革新如下

const createSingletonUtil = (className) => {
  let instance;
  return () => {return (instance ??= new className());
  };
};

咱们封装一个工具函数createSingletonUtil,调用该函数后能够取得任意类的“单例获取函数”

const getSingletonOfA = createSingletonUtil(A);
const a3 = getSingletonOfA();
const a4 = getSingletonOfA();
console.log(a3 === a4); // true

createSingletonUtil 的 TypeScript 实现如下:

class A {}
const createSingletonUtil = <T>(className: new () => T) => {
  let instance: T;
  return () => {return (instance ??= new className());
  };
};

const getSingletonOfA = createSingletonUtil<A>(A);
const a1 = getSingletonOfA();
const a2 = getSingletonOfA();
console.log(a1 === a2);

当然惰性单例也有毛病,对于某些类,如果创立实例须要较长时间,这时在用到的时候再创立恐怕来不及,可能会产生其余副作用,比方造成页面卡顿。在此场景下,在利用初始化时就创立其实例或者会有更好的用户体验

上述两种办法依据不同的业务场景择一应用即可

公众号【明天也要写 bug】(op-bot)发问答疑

退出移动版