單例模式也稱(chēng)為單體模式,規(guī)定一個(gè)類(lèi)只有一個(gè)實(shí)例,并且提供可全局訪(fǎng)問(wèn)點(diǎn);
在讀這篇文章之前,也許你對(duì)單例模式的概念感到模糊或者不清楚,但是其實(shí)在日常的開(kāi)發(fā)中你肯定用到過(guò)單例模式;
JavaScript中沒(méi)有類(lèi)的定義,單例模式的特點(diǎn)是”唯一“和”全局訪(fǎng)問(wèn)“,那么我們可以聯(lián)想到JavaScript中的全局對(duì)象,利用ES6的let不允許重復(fù)聲明的特性,剛好符合這兩個(gè)特點(diǎn);是的,全局對(duì)象是最簡(jiǎn)單的單例模式;
let obj = {
name:"咸魚(yú)",
getName:function(){}
}
上述代碼中可以知道obj就是一個(gè)單例,因?yàn)閛bj剛好就符合單例模式的兩大特點(diǎn):"唯一"和"可全局訪(fǎng)問(wèn)";
但是我們并不建議這么實(shí)現(xiàn)單例,因?yàn)槿謱?duì)象/全局變量會(huì)有一些弊端:
1.污染命名空間(容易變量名沖突)
2.維護(hù)時(shí)不容易管控 (搞不好就直接覆蓋了)
簡(jiǎn)單版單例模式:
分析:只能有一個(gè)實(shí)例,所以我們需要使用if分支來(lái)判斷,如果已經(jīng)存在就直接返回,如果不存在就新建一個(gè)實(shí)例;
let Singleton = function(name){
this.name = name;
this.instance = null;
}
Singleton.prototype.getName = function(){
console.log(this.name);
}
Singleton.getInstance = function(name){
if(this.instace){
return this.instance;
}
return this.instance = new Singleton(name);
}
let winner = Singleton.getInstance("winner"); //winner
console.log(winner.getName());
let sunner = Singleton.getInstance("sunner"); //winner
console.log(sunner.getName())
上面代碼中我們是通過(guò)一個(gè)變量instance的值來(lái)進(jìn)行判斷是否已存在實(shí)例,如果存在就直接返回this.instance,如果不存在,就新建實(shí)例并賦值給instance;
但是上面的代碼還是存在問(wèn)題,因?yàn)閯?chuàng)建對(duì)象的操作和判斷實(shí)例的操作耦合在一起,并不符合”單一職責(zé)原則“;
改良版:
思路:通過(guò)一個(gè)閉包,來(lái)實(shí)現(xiàn)判斷實(shí)例的操作;
閉包警告:不理解閉包的同學(xué)請(qǐng)先學(xué)習(xí)閉包 https://www.cnblogs.com/dengyao-blogs/p/11475575.html
let CreateSingleton = (function(){
let instance = null;
return function(name){
this.name = name;
if(instance){
return instance
}
return instance = this;
}
})()
CreateSingleton.prototype.getName = function(){
console.log(this.name);
}
let winner = new CreateSingleton("winner"); //winner
console.log(winner.getName());
let sunner = new CreateSingleton("sunner"); //winner
console.log(sunner.getName())
代理版單例模式:
通過(guò)代理的形式,將創(chuàng)建對(duì)象的操作和實(shí)例判斷的操作進(jìn)行解耦拆分,實(shí)現(xiàn)更小粒度的劃分,符合”單一職責(zé)原則“;
let ProxyCreateSingleton = (function(){
let instance = null;
return function(name){
if(instance){
return instance
}
return instance = new Singlton(name);
}
})();
let Singlton = function(name){
this.name = name;
}
Singlton.prototype.getName = function(){
console.log(this.name);
}
let winner = new ProxyCreateSingleton("winner");
console.log(winner.getName());
let sunner = new ProxyCreateSingleton("sunner");
console.log(sunner.getName());
上面的代碼中,ProxyCreateSingleton()只負(fù)責(zé)判斷實(shí)例,Singlton只負(fù)責(zé)創(chuàng)建對(duì)象和賦值;
惰性單例模式
我們經(jīng)常會(huì)有這樣的場(chǎng)景:頁(yè)面多次調(diào)用都有彈窗提示,只是提示內(nèi)容不一樣;
這個(gè)時(shí)候我們可以立馬想到是單例模式,彈窗就是單例實(shí)例,提示內(nèi)容是參數(shù)傳遞;我們可以用惰性單例模式來(lái)實(shí)現(xiàn)它;
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<div id="loginBtn">有夢(mèng)想的咸魚(yú)</div>
</body>
<script>
let getSingleton = function(fn) {
var result;
return function() {
return result || (result = fn.apply(this, arguments)); // 確定this上下文并傳遞參數(shù)
}
}
let createAlertMessage = function(html) {
var div = document.createElement('div');
div.innerHTML = html;
div.style.display = 'none';
document.body.appendChild(div);
return div;
}
let createSingleAlertMessage = getSingleton(createAlertMessage);
document.getElementById('loginBtn').onclick=function(){
let alertMessage = createSingleAlertMessage('看來(lái)真的是個(gè)咸魚(yú)');
alertMessage.style.display = 'block';
}
</script>
</html>
惰性單例是指的是頁(yè)面開(kāi)始加載的時(shí)候我們的實(shí)例是沒(méi)有進(jìn)行創(chuàng)建的,是當(dāng)我們點(diǎn)擊頁(yè)面的div之后才開(kāi)始創(chuàng)建實(shí)例(按需創(chuàng)建),這可以提高我們的網(wǎng)頁(yè)性能,加快我們的頁(yè)面渲染速度;