JavaScript 设计模式(一):单例模式
一、基本模式
1、定义:
保证一个类仅有一个实例,并提供一个全局访问点。
2、核心:
使用闭包存储全局实例。
3、实现:
// Root类
function Root(name) {
this.name = name
this.show = function () {
console.log(this.name)
}
}
// 单例函数
function Single() {
var root
return function (name) {
if (!root) {
root = new Root(name)
}
return root
}
}
// 单例函数实例(每个函数维护各自的全局实例)
// (BaseSingle是一个函数)
const BaseSingle = new Single()
const BaseSingle2 = new Single()
// 调用实例
BaseSingle('a').show() // a
BaseSingle('b').show() // a
BaseSingle2('b').show() // b
BaseSingle2('a').show() // b
二、扩展模式
上面的例子,可以设置唯一的全局Root实例。可是当我们想要设置Apple实例时,我们就必须去修改这个函数,也就破坏了开放封闭原则。
我们如何去创建不同类型的全局唯一实例呢?
通过观察,我们可以发现,全局实例是通过 root = new Root(name) 来设置的。
那么,我们可以通过外界传入值的方式,来实现创建不同类型实例的效果吗?
当然可以!这里有两种方式来扩展。
1、传对象类
// Root类
function Root(name) {
this.name = name
this.show = function () {
console.log('Root: ', this.name)
}
}
// Apple类
function Apple(size, kg) {
this.size = size,
this.kg = kg
this.isGood = true
this.show = function () {
console.log('Apple: ', this.size, this.kg, this.isGood)
}
}
// 单例函数
function SingleExt (objClass) {
var instance
return function (...args) {
if (!instance) {
instance = new objClass(...args)
}
return instance
}
}
const extSingle = new SingleExt(Root)
const extSingle2 = new SingleExt(Apple)
extSingle('a').show() // a
extSingle('b').show() // a
extSingle2(1, 2).show() // 1 2 true
extSingle2(3, 4).show() // 1 2 true
2、传函数
// Root类
function Root(name) {
this.name = name
this.show = function () {
console.log('Root: ', this.name)
}
}
// Apple类
function Apple(size, kg) {
this.size = size,
this.kg = kg
this.isGood = true
this.show = function () {
console.log('Apple: ', this.size, this.kg, this.isGood)
}
}
// 单例函数
function SingleExt (fn) {
var instance
return function () {
if (!instance) {
instance = fn.apply(this, arguments)
}
return instance
}
}
const extSingle = new SingleExt((name) => {
return new Root(name)
})
const extSingle2 = new SingleExt((size, kg) => {
return new Apple(size, kg)
})
extSingle('a').show() // a
extSingle('b').show() // a
extSingle2(1, 2).show() // 1 2 true
extSingle2(3, 4).show() // 1 2 true
3、两种方式的比较
你可能会有疑问,通过传入对象类的形式已经可以实现创建不同类型的全局实例了,为什么还需要使用传入函数的形式呢?
答案是:为了易于扩展,实现类似于代理的效果。
例如:市场上定义了,一个苹果的size小于10,则说明是坏的,它的isGood就会设置为false。
如果使用传入对象类的形式,我们只会得到一个初始化的实例,需要额外去设置对象的isGood属性。
如果使用传入函数的形式,我们可以在函数中加各种判定,创建并设置对应的实例,之后返回该实例。
(参考代码如下:)
// Root类
function Root(name) {
this.name = name
this.show = function () {
console.log('Root: ', this.name)
}
}
// Apple类
function Apple(size, kg) {
this.size = size,
this.kg = kg
this.isGood = true
this.show = function () {
console.log('Apple: ', this.size, this.kg, this.isGood)
}
}
// 单例函数
function SingleExt (fn) {
var instance
return function () {
if (!instance) {
instance = fn.apply(this, arguments)
}
return instance
}
}
const extSingle = new SingleExt((name) => {
return new Root(name)
})
const extSingle2 = new SingleExt((size, kg) => {
var apple = new Apple(size, kg)
if (size < 10) { // 如果 size < 10, 说明是坏的,则 isGood = false
apple.isGood = false
}
return apple
})
extSingle('a').show() // a
extSingle('b').show() // a
extSingle2(1, 2).show() // 1 2 false
extSingle2(3, 4).show() // 1 2 false
参考资料:
1、JavaScript中常见的十五种设计模式
2、JavaScript设计原则&&常用设计模式
<<< 上一篇:JavaScript 设计模式(零):设计原则
>>> 下一篇:JavaScript 设计模式(二):策略模式