前言
单例模式:一个类只能有一个实例,即使多次实例化该类,也只返回第一次实例化后的实例对象。也就是说他全局就创建这第一次实例化的实例,后续访问的都是第一次的实例。这种模式常用于那些需要频繁创建和销毁的对象,以减少系统开销并确保资源的合理利用。具体实现:
正文
举个例子,就像我们现在在使用的稀土掘金就有使用这类设计模式:
首先点击登录实例化出登录界面
如果这次我们并没有登录,但是要是要去点赞评论别人的文章还是会弹出登录的界面,而这次以及后续弹出来的登录界面框都是我们第一次点击登录却没登陆实例化出的登录框。这样做的主要就是给浏览器减少了内存消耗和提高了性能。在多窗口或多标签页浏览的情况下,还可以确保所有页面共享相同的登录状态信息,避免了在一个页面登录后,其他页面仍显示未登录状态的问题。下面就举两种实现单例模式的方法:
静态属性方式
var Singleton = function(name){
this.name = name;
}
Singleton.prototype.getName = function(){
console.log(this.name)
}
// 静态方法 属于类的
Singleton.getInstance = function(name){
if(!this.instance){
// 静态的属性
this.instance = new Singleton(name);
}
return this.instance;
}
let obj1=Singleton.getInstance('hjo')
let obj2=Singleton.getInstance('hje')
console.log(obj1==obj2)
上面代码输出的是true,那我们分析一下是怎么实现的把:首先先这里定义了一个构造函数 Singleton,它接受一个参数 name 并将其保存在实例的 name 属性中。然后这个 getName 方法被添加到 Singleton 的原型上,意味着所有 Singleton 的实例都可以调用这个方法。当调用 getName 时,它会打印出实例的 name 属性。第三步是 Singleton 的静态方法,它用于获取单例实例。最后通过判断obj1和obj2来判断后续访问是否是引用的相同对象。
使用闭包
var Singleton = (function() {
// 静态属性(单例实例)
var instance;
// 构造函数
function SingletonConstructor(name) {
this.name = name;
}
// 原型方法
SingletonConstructor.prototype.getName = function() {
console.log(this.name);
};
// 静态方法(获取单例实例)
function getInstance(name) {
if (!instance) {
instance = new SingletonConstructor(name);
}
return instance;
}
// 暴露外部可访问的接口
return {
getInstance: getInstance
};
})();
// 使用单例
let obj1 = Singleton.getInstance('hjo');
let obj2 = Singleton.getInstance('hje');
console.log(obj1 === obj2);
闭包方式在早期JavaScript中较为常见,利用了JavaScript的闭包特性来隐藏实例,但在ES6引入类之后,使用闭包的方式相对较少见。相比于使用闭包来实现单例模式,静态属性方式的代码更简洁更易于理解和维护。
总结
单例模式的优缺点:
优点
资源节约:由于系统中只存在一个实例,因此减少了对系统资源的消耗,特别是那些需要频繁创建和销毁的对象,如数据库连接池、线程池等。
全局访问:提供了一个全局访问点,任何地方都可以轻松获取到这个实例,无需传递引用,简化了对象的使用。
控制实例化:对于需要控制并发访问或资源独占的场景,单例模式可以确保同一时间内只有一个实例在操作共享资源,避免资源冲突。
易于维护和扩展:在需要对实例进行修改或扩展时,只需要在一个地方修改即可,降低了维护成本。
缺点
违反单一职责原则:单例类往往承担了控制实例创建的责任,这可能与它原本的业务逻辑职责相冲突,导致代码不易理解与维护。
不便于测试:由于单例的全局状态,单元测试时难以隔离测试环境,可能会受到其他测试或全局状态的影响。
扩展困难:由于单例类没有抽象层,一旦需要改变实例化的方式或添加新的行为,可能会引起大量的代码修改。
不适用于所有场景:对于需要多个不同配置或状态的实例,单例模式限制了灵活性。
可能造成内存泄漏:在长时间运行的系统中,如果单例持有大量资源,且这些资源没有被及时释放,可能会引发内存泄漏问题。
是否选择用单例模式,需要权衡这些优缺点,考虑具体需求,而不是一味的为了方便简洁。