Eswlnk Blog Eswlnk Blog
  • 资源
    • 精彩视频
    • 破解专区
      • WHMCS
      • WordPress主题
      • WordPress插件
    • 其他分享
    • 极惠VPS
    • PDF资源
  • 关于我
    • 论文阅读
    • 关于本站
    • 通知
    • 左邻右舍
    • 玩物志趣
    • 日志
    • 专题
  • 热议话题
    • 游戏资讯
  • 红黑
    • 渗透分析
    • 攻防对抗
    • 代码发布
  • 自主研发
    • 知识库
    • 插件
      • ToolBox
      • HotSpot AI 热点创作
    • 区块
    • 快乐屋
    • 卡密
  • 乱步
    • 文章榜单
    • 热门标签
  • 问答中心反馈
  • 注册
  • 登录
首页 › 代码发布 › 「代码分享」JavaScript 单例模式的实现与应用

「代码分享」JavaScript 单例模式的实现与应用

Eswlnk的头像
Eswlnk
2024-04-20 18:27:02
「代码分享」JavaScript 单例模式的实现与应用-Eswlnk Blog
智能摘要 AI
单例模式在JavaScript中不一定非要使用类来实现,尽管这是常见的做法。通过闭包或工厂函数也可以达到相同效果,并且更灵活。例如,使用闭包可以确保每次实例化时返回同一个实例: ```javascript var Singleton = (function () { var instance = null; function User(name) { if (instance) return instance; this.name = name; return (instance = this); } User.prototype.getInstance = function (name) { if (!this.instance) { this.instance = new User(this.name); } return this.instance; }; return User; })(); ``` 此外,还可以通过泛用型单例封装来解耦业务逻辑

前言

单例模式在强类型语言中常常是使用一个类来实现,哪怕在ts中,常见的单例示例也是使用class来实现,但是事实上在js中,不一定非得要用类来实现,我们不应该将实现方式看的太死板,使用class也只是手段的一种,在js中我们有很多种方式达到相同的效果。

「代码分享」JavaScript 单例模式的实现与应用-Eswlnk Blog

单例模式的定义是:保证一个类仅有一个实例,并提供一个访问它的全局访问点。

拙劣的模仿

在强类型语言中,会给类一个getInstance的方法用于获取实例对象,在js中我们也可以这么写:

function User(name) {
  this.name = name;
  this.instance = null;
}

User.prototype.getInstance = function (name) {
  if (!this.instance) {
    this.instance = new User(this.name);
  }
  return this.instance;
};

const a = User.getInstance("a");
const b = User.getInstance("b");

console.log(a === b); // true

可以看到我们约定俗成的使用方式下,也可以模仿出相同的效果,但是这种方式有个弊端,他没法通过new去创建实例,且无法使得new出的实例永远是同一个。

为此我们可以使用闭包的方式。

var Singleton = (function () {
  var instance = null;

  function User(name) {
    if (instance) return instance;
    this.name = name;

    return (instance = this);
  }

  User.prototype.getInstance = function (name) {
    if (!this.instance) {
      this.instance = new User(this.name);
    }
    return this.instance;
  };

  return User;
})();

const a = new Singleton("a");
const b = new Singleton("b");

console.log(a === b); // true

通过闭包创建局部变量instance,然后判断局部变量是否有值,这个值就是User的实例对象,返回即可。

我们还可以看一下ts中单例的实现与转化为js后的代码:

class Singleton {
    private static instance: Singleton;

    private constructor() { }

    public static getInstance(): Singleton {
        if (!Singleton.instance) {
            Singleton.instance = new Singleton();
        }
        return Singleton.instance;
    }

    public someMethod() {
        console.log("调用了单例对象的方法");
    }
}

// 使用单例模式
const singletonInstance1 = Singleton.getInstance();
const singletonInstance2 = Singleton.getInstance();

console.log(singletonInstance1 === singletonInstance2); // 输出 true,说明两个实例是同一个
singletonInstance1.someMethod(); // 调用单例对象的方法

转换为js后:

"use strict";
var Singleton = /** @class */ (function () {
    function Singleton() {
    }
    Singleton.getInstance = function () {
        if (!Singleton.instance) {
            Singleton.instance = new Singleton();
        }
        return Singleton.instance;
    };
    Singleton.prototype.someMethod = function () {
        console.log("调用了单例对象的方法");
    };
    return Singleton;
}());
// 使用单例模式
var singletonInstance1 = Singleton.getInstance();
var singletonInstance2 = Singleton.getInstance();
console.log(singletonInstance1 === singletonInstance2); // 输出 true,说明两个实例是同一个
singletonInstance1.someMethod(); // 调用单例对象的方法

你可以发现其实是差不多的原理。

饿汉式和懒汉式

他们的区别就是饿汉式在代码没有被实际用户使用时就已经创建好了对应的实例,而懒汉式则是用户实际用到了才会去创建,上面的代码都是懒汉式的。

泛用型单例封装

在实际的代码中,单例模式的使用,往往除了上面的代码量,还会有很多其他的业务逻辑,我们常常会陷入一个很头疼的选择,如何保证代码的​单一职责​。

理论上单例模式本身是一种职责,而具体的业务是另一种职责,他们应该分开来做最合适,但是在一些强类型语言,或者说在ts中,想要做到这种解耦是很难的,但是在js中,你会发现解耦这种操作,简直小菜一碟。

function getSingleton(fn) {
    var instance = null;

    return function() {
        if (instance) return instance;
        return (instance = fn.apply(this, arguments));
    };
}

function User(name) {
    this.name = name;
}

var userSingleton = getSingleton(User, "a");

const a = userSingleton();
const b = userSingleton();

console.log(a === b); // true

如果我们想要在ts中也实现这种泛用性的单例封装,就得使用一个工厂类来做更加合适:

class SingletonFactory {
    private static instances: { [key: string]: any } = {};

    public static getInstance<T>(clazz: { new(): T }): T {
        const className = clazz.name;
        if (!SingletonFactory.instances[className]) {
            SingletonFactory.instances[className] = new clazz();
        }
        return SingletonFactory.instances[className];
    }
}

// 示例业务类
class BusinessClassA {
    public someMethod() {
        console.log("调用了业务类A的方法");
    }
}

class BusinessClassB {
    public someMethod() {
        console.log("调用了业务类B的方法");
    }
}

// 使用单例工厂类
const instanceA1 = SingletonFactory.getInstance(BusinessClassA);
const instanceA2 = SingletonFactory.getInstance(BusinessClassA);

当然这里投机取巧用了class类的一个name属性,虽然说没什么问题,但是实际上会有大问题,因为我们的代码在实际打包后会编译混淆,可能会有存在同名类的情况,我们可以改用map类型来存储,这样的话可以避免重复的问题,使得代码更加健壮。

class SingletonFactory {
    private static instances: Map<any, any> = new Map();

    public static getInstance<T>(clazz: { new(): T }): T {
        if (!SingletonFactory.instances.has(clazz)) {
            SingletonFactory.instances.set(clazz, new clazz());
        }
        return SingletonFactory.instances.get(clazz);
    }
}

// 示例业务类
class BusinessClassA {
    public someMethod() {
        console.log("调用了业务类A的方法");
    }
}

class BusinessClassB {
    public someMethod() {
        console.log("调用了业务类B的方法");
    }
}

// 使用单例工厂类
const instanceA1 = SingletonFactory.getInstance(BusinessClassA);
const instanceA2 = SingletonFactory.getInstance(BusinessClassA);

const instanceB1 = SingletonFactory.getInstance(BusinessClassB);
const instanceB2 = SingletonFactory.getInstance(BusinessClassB);

console.log(instanceA1 === instanceA2); // 输出 true,说明两个实例是同一个
console.log(instanceB1 === instanceB2); // 输出 true,说明两个实例是同一个

instanceA1.someMethod(); // 调用业务类A的方法
instanceB1.someMethod(); // 调用业务类B的方法

对于不想使用any的人来说,可能会很难受吧,哈哈哈。

本站默认网盘访问密码:1166
本站默认网盘访问密码:1166
JavaScript代码示例
6
0
Eswlnk的头像
Eswlnk
一个有点倒霉的研究牲站长
赞赏
「代码分享」配置 Nuxt3 SEO | 站点地图 sitemap.xml 和 robots.txt
上一篇
「代码分享」深入理解 JavaScript 的发布订阅模式 | 实现灵活的消息通信与事件管理
下一篇

评论 (0)

请登录以参与评论
现在登录
    发表评论

猜你喜欢

  • 小工具开发之EdgeOne免费计划兑换工具
  • 「日志记录」逆向必应翻译网页版API实现免费调用
  • 「渗透分析」如何防范JS注入?nbcio-boot代码审计攻略大公开
  • 「代码分享」第三方平台VIP视频解析API接口
  • 「至臻原创」某系统网站登录功能监测
Eswlnk的头像

Eswlnk

一个有点倒霉的研究牲站长
1108
文章
319
评论
679
获赞

随便看看

「技术分享」解密凡科网滑块验证:逆向JS、base64解码与Selenium应用
2024-04-26 13:03:34
Easywoole框架实战_封装批量更新数据ORM
2022-05-01 1:16:48
「技术教程」定制项目包管理器 | 使用生命周期脚本限制只允许特定包管理器
2023-12-20 23:17:43

专题展示

WordPress53

工程实践37

热门标签

360 AI API CDN java linux Nginx PDF PHP python SEO Windows WordPress 云服务器 云服务器知识 代码 免费 安全 安卓 工具 开发日志 微信 微软 手机 插件 攻防 攻防对抗 教程 日志 渗透分析 源码 漏洞 电脑 破解 系统 编程 网站优化 网络 网络安全 脚本 苹果 谷歌 软件 运维 逆向
  • 首页
  • 知识库
  • 地图
Copyright © 2023-2025 Eswlnk Blog. Designed by XiaoWu.
本站CDN由 壹盾安全 提供高防CDN安全防护服务
蜀ICP备20002650号-10
页面生成用时 0.476 秒   |  SQL查询 30 次
本站勉强运行:
友情链接: Eswlnk Blog 网站渗透 倦意博客 特资啦!个人资源分享站 祭夜博客 iBAAO壹宝头条
  • WordPress142
  • 网络安全64
  • 漏洞52
  • 软件52
  • 安全48
现在登录
  • 资源
    • 精彩视频
    • 破解专区
      • WHMCS
      • WordPress主题
      • WordPress插件
    • 其他分享
    • 极惠VPS
    • PDF资源
  • 关于我
    • 论文阅读
    • 关于本站
    • 通知
    • 左邻右舍
    • 玩物志趣
    • 日志
    • 专题
  • 热议话题
    • 游戏资讯
  • 红黑
    • 渗透分析
    • 攻防对抗
    • 代码发布
  • 自主研发
    • 知识库
    • 插件
      • ToolBox
      • HotSpot AI 热点创作
    • 区块
    • 快乐屋
    • 卡密
  • 乱步
    • 文章榜单
    • 热门标签
  • 问答中心反馈