JavaScript单例模式

JavaScript单例模式

  • 1 什么是单例模式
  • 2 实现一个基础的单例模式
  • 3 透明的单例模式
  • 4 用代理实现单例模式
  • 5 JavaScript 中的单例模式
  • 6 惰性单例

1 什么是单例模式

保证一个类只有一个实例,并提供一个访问它的全局访问点,这就是单例模式。

单例模式是一种常用的模式,有一些对象我们往往只需要一个,比如window对象、全局缓存等。

2 实现一个基础的单例模式

根据单例模式的特点,在实现单例模式时,我们要保证某个类只能创建一个实例对象,那么实现的基本思路就是:用一个变量标识当前是都已经为某个类创建过对象,如果是,在下一次获取该类的实例时,直接返回之前创建的对象,否则创建一个新对象,示例代码如下:

// 定义一个构造函数MyExample,定义属性name与instance
function MyExample(name) {this.name = name;this.instance = null;
}
// 在原型上定义getName方法
MyExample.prototype.getName = function () {console.log(this.name);
};
// 为构造函数定义getInstance方法,用来获取类的实例
MyExample.getInstance = function (name) {// 标识是否创建了实例,如果没有,赋值为新的实例,如果已经创建,返回原来的实例if (!this.instance) {this.instance = new MyExample(name);}return this.instance;
};// 为MyExample类创建实例,看似创建两个实例,实际上都是同一个实例
var e1 = MyExample.getInstance("example1");
var e2 = MyExample.getInstance("example2");console.log(e1 === e2); // true

我们通过MyExample.getInstance来获取 MyExample类的唯一对象,这种方式相对简单,但有一个问题,就是增加了这个类的“不透明性”,MyExample类的使用者必须知道这是一个单例类,跟以往通过new XXX的方式来获取对象不同。

3 透明的单例模式

在上一步中,由于该类的“不透明性”,用户在使用时会有一些困难,因此在本节中实现一个“透明”的单例类,让用户在使用这个类创建对象时,可以和使用其他普通类一样。

接下来实现一个单例类CreateDiv,这个类的作用是在页面创建唯一的div节点,代码如下:

var CreateDiv = (function () {var instance; // 标识是否创建过实例// 创建CreateDiv类var CreateDiv = function (html) {// 如果已经创建过实例,返回当前实例if (instance) {return instance;}// 如果没有创建实例。创建一个div元素,并返回this.init();this.html = html;return (instance = this);};// 定义init方法,目的是创建一个div元素,元素内容由我们自己定义CreateDiv.prototype.init = function () {var div = document.createElement("div");div.innerHTML = this.html;document.body.appendChild(div);};return CreateDiv;
})();var e1 = new CreateDiv("example1");
var e2 = new CreateDiv("example2");console.log(e1 === e2); // true

虽然现在完成了一个透明的单例类的编写,但它同样有一些缺点,为了把instance封装起来,使用了自执行的匿名函数和闭包,并且让这个匿名函数返回真正的构造方法,复杂度高,可读性也变差了。

4 用代理实现单例模式

在上面的例子中,假设我们某天需要利用这个类,在页面中创建千千万万的div,即要让这个类从单例类变成一个普通的可产生多个实例的类,那我们必须得改写CreateDiv构造函数,这种修改会给我们带来不必要的烦恼,因此我们通过引入代理类的方式来解决这个问题

首先在CreateDiv构造函数中,把负责管理单例的代码提出来,使它成为一个普通的创建div的类:

var CreateDiv = function (html) {this.html = html;this.init();
};CreateDiv.prototype.init = function () {var div = document.createElement("div");div.innerHTML = this.html;document.body.appendChild(div);
};

接下来引入代理类proxySingletonCreateDiv

var ProxySingletonCreateDiv = (function () {var instance;return function (html) {if (!instance) {instance = new CreateDiv(html);}return instance;};
})();var e1 = new ProxySingletonCreateDiv("example1");
var e2 = new ProxySingletonCreateDiv("example2");console.log(e1 === e2); // true

上面的例子通过引入代理类的方式完成了一个单例模式的改写,与之前不同的是,现在把负责管理单例的逻辑移到了代理类proxySingletonCreateDiv中。这样一来,CreateDiv就变成了一个普通的类,它跟 proxySingletonCreateDiv组合起来可以达到单例模式的效果。

5 JavaScript 中的单例模式

前面提到的几种单例模式的实现,更多的是接近传统面向对象语言中的实现,单例对象从“类”中创建而来,在以类为中心的语言中,这是很自然的做法。比如在Java中,如果需要某个对象,就必须先定义一个类,对象总是从类中创建而来的。

JavaScript其实是一门无类语言,创建对象的方法非常简单,既然我们只需要一个“唯一”的对象,为什么要为它先创建一个“类”呢,传统的单例模式实现在JavaScript中并不适用。

单例模式的核心是确保只有一个实例,并提供全局访问。

全局变量不是单例模式,但在JavaScript开发中,我们经常会把全局变量当成单例来使用。例如:

var a = {}; 

当用这种方式创建对象a时,对象a确实是独一无二的。如果a变量被声明在全局作用域下,我们可以在代码中的任何位置使用这个变量,这样就满足了单例模式的两个条件。但是全局变量存在很多问题,它很容易造成命名空间污染。

我们可以采用以下几种方式降低全局变量带来的命名污染:

1、使用命名空间,适当地使用命名空间,并不会杜绝全局变量,但可以减少全局变量的数量,最简单的方法依然是用对象字面量的方式:

var namespace1 = {a: function () {alert(1);},b: function () {alert(2);},
};

2、使用闭包封装私有变量,这种方法把一些变量封装在闭包的内部,只暴露一些接口跟外界通信:

var user = (function () {var __name = "sven",__age = 29;return {getUserInfo: function () {return __name + "-" + __age;},};
})();

我们用下划线来约定私有变量__name__age,它们被封装在闭包产生的作用域中,外部是访问不到这两个变量的,这就避免了对全局的命令污染。

6 惰性单例

惰性单例指的是在需要的时候才创建对象实例。

惰性单例是单例模式的重点,实例对象instance总是在我们调用MyExample.getInstance的时候才被创建,而不是在页面加载好的时候就创建,示例代码如下:

MyExample.getInstance = (function () {var instance = null;return function (name) {if (!instance) {instance = new MyExample(name);}return instance;};
})();

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/68767.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

(位运算) 剑指 Offer 15. 二进制中1的个数 ——【Leetcode每日一题】

❓ 剑指 Offer 15. 二进制中1的个数 难度:简单 编写一个函数,输入是一个无符号整数(以二进制串的形式),返回其二进制表达式中数字位数为 ‘1’ 的个数(也被称为 汉明重量).)。 提示&#xff…

计算机网络自顶向下-web页面请求历程

1. 准备: DHCP、 UDP、 IP 和以太网 假定 Bob 启动他的便携机,然后将其用一根以太网电缆连接到学校的以太网交换机 , 交换机与学校的路由器相连。学校的路由器与一个 ISP 连接, 本例中 ISP 为 comcast.net ,为学校提供了 DNS 服务…

开发前期准备工作

开发前期准备工作 文章目录 开发前期准备工作0 代码规范0.1 强制0.2 推荐0.3 参考dao:跟数据库打交道service:业务层,人类思维解决controller:抽象化 0.4 注释规范0.5 日志规范0.6 专有名词0.7 控制层统一异常统一结构体控制层提示…

java线程和go协程

一、线程的实现 线程的实现方式主要有三种:内核线程实现、用户线程实现、用户线程加轻量级进程混合实现。因为自己只对java的线程比较熟悉一点,所以主要针对java线程和go的协程之间进行一个对比。 线程模型主要有三种:1、内核级别线程&#…

Unittest自动化测试框架vs Pytest自动化测试框架

引言   前面一篇文章Python单元测试框架介绍已经介绍了python单元测试框架,大家平时经常使用的是unittest,因为它比较基础,并且可以进行二次开发,如果你的开发水平很高,集成开发自动化测试平台也是可以的。而这篇文章…

如何培养潜在客户?看完这篇你就懂了

图片来源于:SaleSmartly官网 有效的潜在客户培育策略将帮助您将更多潜在客户转化为付费客户。 但是,这并不总是那么容易——您必须与其他公司争夺受众的注意力,并向您的领导证明为什么值得投资您的产品或服务。在本文中,我将向您展…

docker安装在linux下的docker安装操作步骤完整版

参考文档:http://wed.xjx100.cn/news/151901.html?actiononClick 第一步,卸载历史版本。这一步是可选的,如果之前安装过旧版本的Docker,可以使用如下命令进行卸载: yum remove docker \docker-client \docker-client…

STM32 RTC实验

RTC时钟简介 STM32F103的实时时钟(RTC)是一个独立的定时器。 STM32的RTC模块拥有一组连续计数的计数器,在相对应的软件配置下,可提供时钟日历的功能。 修改计数器的值可以重新设置系统的当前时间和日期。 RTC模块和时钟配置系统…

redis 报错 Redis protected-mode 配置文件没有真正启动

(error) DENIED Redis is running in protected mode because protected mode is enabled Redis protected-mode 是3.2 之后加入的新特性,在Redis.conf的注释中,我们可以了解到,他的具体作用和启用条件 链接redis 时只能通过本地localhost …

iKeyPrime完美解4G信号,可以登录iCloud,有消息通知,支持最新iOS16.6。

iKeyPrime是一款绕过激活锁界面的解锁工具,可以激活所有iPhone苹果手机,二网/三网恢复信号,并且支持插卡接打电话、收发短信、4G流量上网,支持iCloud登录,有消息通知,支持iPhone5S~X的所有型号,…

精益制造、质量管控,盛虹百世慧共同启动MOM(制造运营管理)

百世慧科技依托在电池智能制造行业中的丰富经验,与盛虹动能达成合作,为其提供MOM制造运营管理平台,并以此为起点,全面提升盛虹动能的制造管理水平与运营体系。 行业困境 中国动力电池已然发展为全球最大的电池产业,但…

[ROS]yolov7部署ROS

Yolov7是一种基于PyTorch深度学习框架的目标检测算法,具有高精度和快速的特点,被广泛应用于机器人领域。将Yolov7部署到ROS中可以方便地实现机器人对环境的感知和理解。 在部署Yolov7到ROS之前,需要准备以下环境和工具: Ubuntu …

Netty—FuturePromise

Netty—Future&Promise 一、JDK原生 Future二、Netty包下的 Future三、Promise1、使用Promise同步获取结果2、使用Promise异步获取结果.3、使用Promise同步获取异常 - sync & get4、使用Promise同步获取异常 - await5、使用Promise异步获取异常 在异步处理时&#xff0…

什么是 API 以及电子商务网站为何使用它们

从技术上讲,API是应用程序编程接口的首字母缩写,被认为是构建应用软件的一组协议。实际上,API 是让人们保持数字联系的大部分基础。 从我们手机上的应用程序到复杂的飞行控制系统,API 将原本完全不同的系统组合在一起以创建单一平…

Img标签的src地址自动拼接本地域名(localhost:8080)导致图片不显示问题

摘要:做Vueelement ui项目的时候,发现使用element ui的upload上传图片时,不显示的问题。我项目的图片是上传到七牛云,长传成功后返回存储在七牛云中的地址。后面发现是因为返回的地址是外部地址,需要完整的URL&#xf…

spring @value 注入static 注入静态变量方法

对于spring static变量 下面给大家介绍spring不能注入static变量的原因,具体详情如下所示: Spring 依赖注入 是依赖 set方法 set方法是 是普通的对象方法 static变量是类的属性 只能在setAppId方法上加注解,另外class需要加 Component等…

【菜鸡学艺–Vue2–001】模板语法声明式渲染

【菜鸡学艺–Vue2–001】模板语法&声明式渲染 🦖我是Sam9029,一个前端 Sam9029的CSDN博客主页:Sam9029的博客_CSDN博客-JS学习,CSS学习,Vue-2领域博主 **🐱‍🐉🐱‍🐉恭喜你,若此文你认为写…

Android大厂需要刷的(999道)面试题

想必大家都在为今年的金九银十做准备,今年也是最为艰难的一年。作为程序员从未感觉到如此艰难,身边不是被辞退就是找不到工作。先不说2023年应届生毕业即失业,作为开发15年的老Android程序员,现在也在和300个人挣一个岗位。 肉少…

使用Dbeaver连接GaussDB

1.下载DBeaver,官网地址 2.安装软件,打开软件,点击数据库->驱动管理器,具体操作如下图: 3、选择新建后进行参数设置,如下图: 具体参数如下图 驱动名称: GS #随便定义 驱动类型&#…

Docker-安装(Linux,Windows)

目录 前言安装版本Docker版本说明前提条件Linux安装使用YUM源部署获取阿里云开源镜像站YUM源文件安装Docker-ce配置Docker Daemon启动文件启动Docker服务并查看已安装版本 使用二进制文件部署 Windows安装实现原理安装步骤基本使用 参考说明 前言 本文主要说明Docker及其相关组…