主题
单例模式
概念
单例模式:保证一个类仅有一个实例,并提供一个访问它的全局访问点
标准单例实现方案
实现的思路是:构造函数,在自身上创建一个变量,或者闭包,记录是否已经创建实例了
ES5
javascript
function User(name) {
if (!User._instance) {
this.name = name
User._instance = this
}
return User._instance
}
const u1 = new User('1')
const u2 = new User('2')
console.log(u1 === u2, u1.name, u2.name) // true 1 1
ES6
javascript
class User{
constructor(name) {
if (!User._instance) {
this.name = name
User._instance = this
}
return User._instance
}
}
const u1 = new User('1')
const u2 = new User('2')
console.log(u1 === u2, u1.name, u2.name) // true 1 1
闭包
javascript
const User = (function() {
let _instance
function _User(name) {
this.name = name
}
return function (name) {
if (!_instance) {
_instance = new _User(name)
}
return _instance
}
})()
const u1 = new User('1')
const u2 = new User('2')
console.log(u1 === u2, u1.name, u2.name) // true 1 1
基于闭包思路,构造一个通用模式
javascript
function Single(cons) {
let _instance
return function(...arg) {
if (!_instance) {
_instance = new cons(...arg)
}
return _instance
}
}
function User(name) {
this.name = name
}
const SingleUser = Single(User)
const u1 = new SingleUser('1')
const u2 = new SingleUser('2')
console.log(u1 === u2, u1.name, u2.name) // true 1 1
还有一种,导出 getInstance 方法的思路
javascript// es5 function User(name) { this.name = name } User.getInstance = (function() { let instance return function(name) { if (!instance) { instance = new User(name) } return instance } })()
javascript// es6 class User { constructor(name) { this.name = name } static getInstance() { if (!User._instance) { User._instance = new User() } return User._instance } }
JavaScript 里的单例
单例模式的核心:确保只有一个实例,并提供全局访问
- 上面借鉴其他语言,实现的标准单例
- es module,module 导出访问的入口,第一次被 import 时,才执行,后面 import 会引用同样的入口
- 创建一个全局变量,并提供一个全局访问的入口(但非常不推荐,污染全局)
应用
vuex
利用 es module,实现注册的单例
javascript
// src/store.js
let Vue;
export class Store {
constructor(options = {}) {
if (!Vue && typeof window !== 'undefined' && window.Vue) {
install(window.Vue)
}
}
}
export function install (_Vue) {
if (Vue && _Vue === Vue) {
if (__DEV__) {
console.error(
'[vuex] already installed. Vue.use(Vuex) should be called only once.'
)
}
return
}
Vue = _Vue
applyMixin(Vue)
}
全屏弹窗
javascript
class FullLoading {
constructor() {
if (!FullLoading.instance) {
const dom = document.createElement('div')
dom.style.zIndex = '999'
dom.style.width = '100vw'
dom.style.height = '100vh'
dom.style.backgroundColor = 'rgba(0, 0, 0, 0.4)'
dom.style.display = 'none'
dom.style.lineHeight = '100vh'
dom.style.textAlign = 'center'
dom.innerHTML = 'loading...'
document.body.appendChild(dom)
this.dom = dom
FullLoading.instance = this
}
return FullLoading.instance
}
static getInstance() {
if (!FullLoading.instance) {
FullLoading.instance = new FullLoading()
}
return FullLoading.instance
}
open() {
this.dom.style.display = 'block'
}
close() {
this.dom.style.display = 'none'
}
}
可以通过 new 创建,也可以通过 getInstance 获取,拿到的都是同一个 instance
javascript
new FullLoading().open()
setTimeout(() => {
FullLoading.getInstance().close()
}, 2000)