单例模式的定义是: "保证类只有一个实例, 并提供一个能全局访问的接口"。
从一段仿java的代码说起
首先我们参照java 写一段js的单例
var SingleTon function(name){
this.name = name;
this.inctance = null;
}
SingleTon.prototype.getName = function(){
console.log(this.name)
}
SingleTon.prototype.getInstance = function(name) {
if(!this.instance) {
this.instance = new SingleTon(name);
}
return this.instance;
}
var a = SingleTon.getInstance('a');
var b = SingleTon.getInstance('b');
console.log(a===b)
虽然这是一个最简单的单例, 但是很明显, 这个类的不透明性与正统的js写法是相悖的。 在js中, 获取一个类的写法是 new XXX, 这里要 SingleTon.getInstance 来获取, 这种生搬java的模式,对于js而言, 并没有太大的意义。
结合业务场景,使用代理实现单例模式
var createDiv = function(name){
this.html = html;
this.init();
}
instance.ptototype.init = function(){
var div = document.createElement('div');
div.innerHtml = this.html;
document.body.appendChild(div);
}
var proxySingle = (function(){
var instance;
return function(html){
if(!this.instance){
instance = new createDiv();
}
return instance;
}
})()
var a = new proxySingle('a');
var a = new proxySingle('b');
将管理单例模式, 移动到了代理中, createDiv和proxySingle组合实现了单例模式。
正统的js写法实现单例模式
由于js是一门无类的语言,以上生搬硬套的写法,能否归属到奇技淫巧之列, 长此以往, 必然会走上邪路。
js 创建对象的方法很简单, 单例的核心只是确保只有一个实例, 并且全局可以访问, 我们为何一定要创建一个类呢?
var a = {};
a实际上就是一个单例, 但是减少全局变量污染, 我们可以
- 使用命名空间
const nameSpace = {
a: function(){
console.log('a')
},
b:function(){
console.log('b')
}
}
- 使用闭包封装变量
const user = (function(){
var __name = 'zehui',
__age = 29;
return {
getUserInfo(){
return `${__name}{__age}`
}
}
})()
总结
因为语言的差异性, 相比于传统的实现单例的模式, js有更适合的方法。 但是最然简单, 但是实用性大大超乎我们的想象, 单例在 Vue 的源码中, 也有大量的运用。而将创建对象和管理单例的职责划分开, 使用组合的方式来实现。