本文汇总了JavaScript中实现单例模式的六种方案,涵盖ES5函数与ES6 Class两种技术路线。通过instanceof判断、静态方法、闭包及包装对象等技巧,确保全局仅有一个实例。文章详细解析了各方案的代码实现、内存占用及命名空间管理优势,帮助开发者解决多人开发中的命名冲突与资源优化问题。
前言
近期在复习设计模式中的创建型模式时,发现JavaScript实现单例模式存在多种方案。经过整理与总结,本文列出以下六种实现方式供参考。
内容主要划分为基于ES5函数(Function)和基于ES6类(Class)的两大部分。
单例模式的概念
单例模式旨在系统中保持唯一实例,相当于一个全局变量。在团队协作开发中,不同页面可能需要实现相似功能(如表单验证),若命名相同易产生冲突。单例模式能有效解决此类命名冲突问题。
- 一个实例只生产一次
- 保证一个类仅有一个实例,并提供一个访问它的全局访问点。
其优点主要体现在以下方面:
1. 单例模式可声明一个命名空间,生成唯一的全局变量。可以通过声明对象的方式来定义命名空间:
var mapleTao={ name:"mapleTao",init:function(){console.log(this.name)}};
这种形式与对象字面量类似。其中name和init是对象的属性,通过mapleTao.name可获取属性值,通过mapleTao.init()可调用init方法。在处理多需求页面及多人协作开发时,这种方式能有效解决命名冲突,便于代码维护与控制。
2. 单例模式仅在全局中声明一个变量。在JavaScript中,若定义函数如function aa(){},会在window对象中生成名为aa的变量。若功能封装中创建大量函数,会导致window中产生众多变量,占用更多内存单元。由于全局变量作用域广泛,在多个处理函数中可能被修改,导致bug难以快速定位。而通过单例模式创建的对象变量,能更快速地定位并解决问题,从而大幅减少修复时间及系统加载时间。
3. 在实现同一功能时,相比通过new关键字新创建对象,单例模式在内存和资源占用上更具优势。
方式1
利用instanceof判断是否使用new关键字调用函数进行对象的实例化。
function User() {
if (!(this instanceof User)) {
return
}
if (!User._instance) {
this.name = '无名'
User._instance = this
}
return User._instance
}
const u1 = new User()
const u2 = new User()
console.log(u1===u2);// true
方式2
在函数上直接添加方法属性调用生成实例。
function User(){
this.name = '无名'
}
User.getInstance = function(){
if(!User._instance){
User._instance = new User()
}
return User._instance
}
const u1 = User.getInstance()
const u2 = User.getInstance()
console.log(u1===u2);
方式3
使用闭包,改进方式2。
function User() {
this.name = '无名'
}
User.getInstance = (function () {
var instance
return function () {
if (!instance) {
instance = new User()
}
return instance
}
})()
const u1 = User.getInstance()
const u2 = User.getInstance()
console.log(u1 === u2);
方式4
使用包装对象结合闭包的形式实现。
const User = (function () {
function _user() {
this.name = 'xm'
}
return function () {
if (!_user.instance) {
_user.instance = new _user()
}
return _user.instance
}
})()
const u1 = new User()
const u2 = new User()
console.log(u1 === u2); // true
此处可将闭包部分的代码单独封装为一个函数。
在频繁使用到单例的情况下,推荐使用类似此方法的方案,当然内部实现可以采用上述任意一种。
function SingleWrapper(cons) {
// 排除非函数与箭头函数
if (!(cons instanceof Function) || !cons.prototype) {
throw new Error('不是合法的构造函数')
}
var instance
return function () {
if (!instance) {
instance = new cons()
}
return instance
}
}
function User(){
this.name = 'xm'
}
const SingleUser = SingleWrapper(User)
const u1 = new SingleUser()
const u2 = new SingleUser()
console.log(u1 === u2);
方式5
在构造函数中利用new.target判断是否使用new关键字。
class User{
constructor(){
if(new.target !== User){
return
}
if(!User._instance){
this.name = 'xm'
User._instance = this
}
return User._instance
}
}
const u1 = new User()
const u2 = new User()
console.log(u1 === u2);
方式6
使用static静态方法。
class User {
constructor() {
this.name = 'xm'
}
static getInstance() {
if (!User._instance) {
User._instance = new User()
}
return User._instance
}
}
const u1 = User.getInstance()
const u2 = User.getInstance()
console.log(u1 === u2);
总结
到此这篇关于JS实现单例模式的文章就介绍到这了,更多相关JS单例模式内容请搜索本站以前的文章或继续浏览下面的相关文章希望大家以后多多支持本站!