JavaScript单例模式解析:从惰性创建到Vue源码中的去实例化实践
本文深入解析JavaScript单例模式的两种分类:普通单例与惰性单例。通过DOM创建案例演示如何利用闭包实现“用时才创建”的惰性单例,并进一步结合Vue源码,分析Vue.use方法如何通过检查已安装插件数组实现插件的单例安装,探讨单例模式中“去实例化”只保留“单”逻辑的设计思想。
一、单例模式的分类
单例模式的核心在于确保一个特定的运行环境中仅存在一个实例,且该实例可被当前环境访问。这个“环境”的范围可大可小:微观上可以是某个函数作用域或块级作用域,宏观上则可以是全局的window或global对象。若依据实例生成的时机对单例模式进行划分,主要包含以下两类:
- 普通单例模式:在环境初始化阶段即完成实例的创建。
- 惰性单例模式:仅在特定触发条件下才进行实例的创建。
二、惰性单例模式
基于单例模式的定义——即一个环境中有且仅有一个实例,我们可以推导出一种特殊形态:只有当该实例真正被使用时,才执行创建操作。这种模式被称为惰性单例模式。
- 所谓“惰性”,是指实例的创建行为被推迟到实际使用时刻。
- 所谓“单例”,是指确保当前作用域内始终只存在唯一的实例对象。
弹窗案例:在页面打开的时候,页面中有登录按钮,在点击时,需要创建一个dom节点。
(1)首先,实现一个功能函数:创建节点的函数
var domFun = function () {
var dom = document.createElement('div');
dom.innerHTML = '登录窗口demo'
dom.style.display = 'none';
document.body.appendChild(dom);
return dom;
}
(2)其次,创建管理单例的函数
var createSingle = function (fn) {
var result;
return function () {
return result || (result = fn.apply(this, arguments));
}
}
利用闭包机制,使变量result在当前作用域内持久存在而不被销毁。若result已存在,则直接返回该值;若不存在,则调用传入的功能函数fn,并将结果赋值给result。这一机制确保了当前环境中始终只有一个实例。 (3)将创建节点的函数和管理单例的函数糅合
var createSingleDom = createSingle(domFun)
(4)最后,在点击按钮的时候,进行实例的创建
var loginBtn = document.querySelector('.login')
loginBtn.onclick = function () {
var loginDom = createSingleDom()
console.log(loginDom)
loginDom.style.display = 'block'
}
上述流程实现了仅在点击登录按钮时才生成节点的效果,体现了惰性单例的特征。
该过程将惰性实例的创建逻辑拆解为多个步骤,提升了代码的可修改性和可维护性。如果后续需要创建的不再是div节点,而是image图片、iframe嵌套网页或倒计时组件等其他内容,只需替换步骤(1)中的具体实现即可。
在此示例中,我们严格遵循了“单一实例”这一核心约束。
二、普通单例模式
在vue中的应用:我们知道vue是一个渐进式框架,主要实现视图的渲染,其他的功能可以可以通过第三方插件按需引入,比如路由插件vue-router和全局状态管理vuex。
- 路由
vue-router的使用
import Vue from "vue";
import VueRouter from "vue-router";
Vue.use(VueRouter);
- 状态
vuex的使用
import Vue from "vue";
import Vuex from "vuex";
Vue.use(Vuex);
vue通过Vue.use(Vuex)的方式进行vue-router和vuex的安装,Vue.use的代码如下:
Vue.use = function (plugin: Function | Object) {
const installedPlugins = (this._installedPlugins || (this._installedPlugins = []))
if (installedPlugins.indexOf(plugin) > -1) {
return this
}
// 去掉参数列表中的第一个
const args = toArray(arguments, 1)
// 将Vue作为this推入到参数的首位
args.unshift(this)
if (typeof plugin.install === 'function') {
plugin.install.apply(plugin, args)
} else if (typeof plugin === 'function') {
plugin.apply(null, args)
}
installedPlugins.push(plugin)
return this
}
可以看出,首次执行vue.use(xxx)的时候,会将该插件推入到数组installedPlugins中去,再次执行vue.use(xxx)的时候,installedPlugins.indexOf(plugin) > -1为true,则终止后续逻辑的执行。也就说,vue.use(xxx)只会让该插件执行一次install的安装。
再看 const installedPlugins = (this._installedPlugins || (this._installedPlugins = [])),是不是发现,这里也是单例模式的影子,当this._installedPlugins不存在时为其赋值为[],当前存在时直接返回。
单例模式,去掉具体的例,就剩单模式。就像当前例子中,install只执行一次,this._installedPlugins只初始化一次一样,就体现了一个单字。那这算不算单(例)模式呢?
总结
单例模式是不是可以去实例,只留单呢?这样,很多只执行一次的逻辑也就和单例模式能搭上关系了。仁者见仁智者见智,能不能去,纯属个人想法。因为,优秀的单例模式,是经过无数的程序案例得出的经验性汇总。
以上就是JavaScript单例模式能不能去实例只留单原理解析的详细内容,更多关于JavaScript单例模式去实例的资料请关注本站其它相关文章!
怎么在React或Vue前端中使用Cube.js Client获取数据
2026-11-03 09:24:09
Vue.js 新手入门:怎么使用 Vite 快速搭建第一个项目
2026-11-02 10:30:55
Vue 项目中封装 heatmap.js:生命周期管理与内存释放
2026-11-02 09:17:40
uni-app 自定义组件开发与通信机制详解
2026-11-01 02:03:46
Vue.js 项目中 Mock 数据的三种实现方案
2026-10-31 17:23:18
