JS单例模式

单例模式

一、基本思想

确保只有一个实例,并提供全局访问。

用一个变量来标志当前是否已经为某个类创建过对象

  • 如果已经创建过,则直接返回之前创建的对象
  • 如果没有创建过,则创建并返回

二、明确定义一个单例

用户必须以单例设计者指定的方法来创造新实例/获取已有实例,缺点在于:用户需要知道这个是指定的方法,即不能用 new 操作符像其他对象一样创造实例

基本思想的实现方式1:用一个 instance 变量来控制,每次新建的时候做一次判断

// 构造函数
var Singleton = function (name) {
	this.name = name
	this.instance = null
}

// 创建一个实例
Singleton.prototype.createOrGet = function (name) {
	// 如果没有创建过,则创建并返回
	if (!this.instance) {
		return this.instance = new Singleton(name)
	}
	// 如果已经创建过,则直接返回之前创建的对象
	return instance
}

基本思想的实现方式2:利用IIFE和闭包来保存已创建实例

  • 第一次创建之前,instance 变量设置为 null 代表还没有实例
  • 由于闭包的性质,返回的函数一直存在,故这个外层函数的作用域也一直存在,instance当然也一直存在
  • 在下文instance被实例化之后并一直存在,代表已经有了实例
var Singleton = function(name) {
	this.name = name
}

// createOrGet 创建立即执行,返回内部的函数 function (name) {...}
Singleton.createOrGet = (function(){
	var instance = null
	return function (name) {
		if (!instance) {
			return instance = new Singleton(name)
		}
		return instance
	}
})()

三、用代理包装一个实例

假设 SingletonObject 类是我们需要的一个单例模式的类
我们希望可以通过 new 操作符来创建或获得现有实例

var SingletonObject = function(name) {
	this.name = name
}

用 Proxy 类来包装创建SingletonObject实例的过程

var Proxy = (function(){
	var instance = null
	return function (name) {
		if (!instance) {
			return instance = new SingletonObject (name)
		}
		return instance
	}
})()

现在可以像其他对象一样,用 new 操作符来创建实例
测试一下:

var a = new Proxy('a') // SingletonObject {name: "a"}
var b = new Proxy('b') // SingletonObject {name: "a"}
a === b // true

四、单一职责原则:抽离通用逻辑

创建单例的通用逻辑:

  • 如果是初次创建,即创建实例并返回
  • 如果是再次创建,则直接返回现有实例
if (!instance) {
	// create new instance
}
return instance

那么我们可以将这段代码的分离出来
提供上文 create new instance 部分的方法 fn

// 抽离出创造单例的通用逻辑
var getSingle = function (fn) {
	var instance = null
	return function () {
		if (!instance) {
			instance = fn.apply(this, arguments) // arguments是闭包的参数
		}
		return instance
	}
}

有了 getSingle,我们就可以正常的编写对象的构造函数

// 各种各样的创造实例的方法
// LoginWindow constructor
var LoginWindow = function (name) {
	var loginWindow = document.createElement('div')
	loginWindow.innerHTML = `我是登录窗口${name}`
	document.body.appendChild(loginWindow)
	return loginWindow
}

// Teacher constructor
function Teacher(name) {
	this.name = name
}

// Car constructor
var Car = function (name) {
	var car = new Object()
	car.name = name
	return car
}

有了一些正常的构造函数,我们就可以将其作为 不变(单例的逻辑)中的变数(不同的新建实例方法),给到 getSingle 通用逻辑中:

var createSingleLoginWindow = getSingle(LoginWindow)
var createSingleTeacher = getSingle(Teacher)
var createSingleCar = getSingle(Car)

测试一下是否通用,是否能跑通:

var login1 = createSingleLoginWindow('1') // <div>我是登录窗口1</div>
var login2 = createSingleLoginWindow('2') // <div>我是登录窗口1</div>
login1 === login2 // true

// 可能不能用function XXX(name) {} 形式的创建实例的方法
var teacher1 = createSingleTeacher('alice') // undefined
var teacher2 = createSingleTeacher('bob')   // undefiend

var car1 = createSingleCar('benz') // {name: "benz"} 
var car2 = createSingleCar('bmw')  // {name: "benz"}
car1 === car2 // true

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值