如果心愿本人的代码更优雅、可维护性更高以及更简洁,往往离不开 设计模式 这一解决方案。
在 JS 设计模式中,最外围的思维:封装变动(将变与不变拆散,确保变动的局部灵便,不变的局部稳固)。
单例模式
那么来说说第一个常见的设计模式:单例模式。
单例模式保障一个类仅有一个实例,并提供一个拜访它的全局拜访形式,为了解决一个全局应用的类频繁被创立和销毁、占用内存的问题。
ES5 中通过闭包
在 ES5 中,能够应用闭包(函数外部返回函数被外界变量所援用,导致这个函数外面的变量无奈被开释,就构建成闭包)来保留这个类的实例。
var Singeton = (function(){
var instance;
function User(name,age){
this.name=name;
this.age=age;
}
return function(name,age){if(!instance){instance = new User(name,age)
}
return instance
}
})()
此时这个实例一旦生成,每次都是返回这个实例,且不会被批改,能够看到上面的代码,当给 User 对象初始赋值 name:alice,age:18 时,当前再赋值便有效了,以及每次返回都是初始的实例对象。
ES6 中应用类的动态属性
以上代码应用 ES6 语法来实现,通过类的动态属性来保留惟一的实例对象。
class Singeton {constructor(name,age){if(!Singeton.instance){
this.name = name;
this.age = age;
Singeton.instance = this;
}
return Singeton.instance;
}
}
创立形式依然是一样的,通过 new 关键字创立类的实例对象。
案例
那这样一种设计模式在开发中理论有什么用处呢?咱们试想这样一个业务场景:拜访网站时,很久没有操作页面,此时受权过期,当咱们点击页面上的任何一个中央,都会弹出一个登录框。
那么这个登录框,是全局惟一的,不会存在多份,也不会互相冲突,所以不须要每次都创立一份,保留初始那一份就够了。
提前创立节点
咱们可能会想到首先在页面中提前创立节点,编写好页面款式,最初通过管制元素的 display 属性来达到显示和暗藏的成果。
<div class="modal"> 登录对话框 </div>
<button id="open"> 关上 </button>
<button id="close"> 敞开 </button>
<style>
.modal {
display: none;
/* 其余布局代码省略 */
}
</style>
<script>
document.querySelector("#open").onclick = function(){const modal = document.querySelector('.modal')
modal.style.display = 'block'
}
</script>
这样能够实现需要,全局只有一个登录框,每次都展现同一个。但问题是 dom 元素从一开始它创立好并增加到 body 中,无论是否须要用到,如果有些场景不须要登陆,那么这里初始渲染就会节约空间。
单例模式
那如果不须要初始渲染,仅当须要时才应用,并且每次都返回同一个实例的单例模式应该如何实现呢?
咱们能够这样解决
<!-- 去除 class 为 modal 的标签,动态创建 -->
<script>
const Modal = (function(){
let instance = null
return function(){if(!instance){instance = document.createElement("div")
instance.innerHTML = "登录对话框"
instance.className = "modal"
instance.style.display = "none"
document.body.appendChild(instance)
}
return instance
}
})()
document.querySelector("#open").onclick = function(){
// 创立 modal,如果放在里面,一开始就会创立元素
const modal = Modal()
// 显示 modal
modal.style.display = "block"
}
document.querySelector("#close").onclick = function(){const modal = Modal()
modal.style.display = "none"
}
</script>
尽管下面的形式能够达到成果,然而创建对象和治理单例的逻辑都放在了对象外部,是有些凌乱的。并且如果下次须要创立页面中惟一的 iframe,或者 script 标签,就得将以上函数照抄一遍。
通用单例
首先拆分函数逻辑,将执行创建对象的逻辑拿进去
const createLayer = function(){let div = document.createElement("div")
div.innerHTML = "登录对话框"
div.className = "modal"
div.style.display = "none"
document.body.appendChild(div);
return div;
}
const Modal = (function(){
let instance = null
return function(){if(!instance){instance = createLayer()
}
return instance
}
})()
以上批改后代码逻辑就更为清晰,但此时还不反对通用化的创立别的组件,这时候咱们想想如何将创立单例的办法进行一些优化,是否能够将单例须要执行的函数抽象化。
const createSingle = (function(fn){
let instance;
return function(){return instance || ( instance = fn.apply(this, arguments))
}
})()
这样革新后,如果存在创立 iframe 的办法,也能够间接应用。
const createIframe = function() {const iframe = document.createElement('iframe');
iframe.style.display = 'none';
document.body.appendChild(iframe);
return iframe
}
const singleIframe = createSingle(createIframe)
document.querySelector("#open").onclick = function(){const iframe = singleIframe()
iframe.style.display = 'block'
}
理论利用
以上都是咱小打小闹的试用,那再来看看社区中一些十分棒的实现吧~ 比方:React 中罕用的状态管理工具 Redux 就应用到了单例模式,它有这样一些要求。
- 繁多数据源:整个利用的 state 只存在于惟一一个 store 中。
- State 是只读的:不要间接扭转 state 的值,惟一扭转 state 的办法就是触发 action。
- reducer 是纯函数:须要编写纯函数 reducer 来批改 state 的值。
来看看 Redux 的源码,为了便于浏览已删减局部逻辑判断和正文,能够看到通过 store 的 getState 办法每次获取闭包中的 currentState。
单例模式在内存中只有一个实例,能够缩小内存开销,同时还能在零碎设置全局的拜访点,优化和共享资源。
以上就是单例模式的相干介绍。更多无关 前端
、 设计模式
的内容能够参考我其它的博文,继续更新中~