深色模式
单例模式
单例模式(Singleton Pattern)在应用程序运行期间,单例模式只会在全局作用域下创建一次实例对象,让所有需要调用的地方都共享这一单例对象。比如点击某个按钮出现一个弹窗。
其原理无非是创建实例时检查是否已经存在了当前类型实例对象,存在则返回,否则创建一个新的实例对象。
实现
1. 透明版单例模式
通过 new 的方式来获取单例。使用闭包保存 instance 实例。
下面的例子模拟了在一个系统中只能有一个 管理员的情况
js
const UserAdmin = (function () {
let instance;
return function UserAdmin(name) {
if (!instance) {
this.name = name;
instance = this;
}
return instance;
};
})();
UserAdmin.prototype.setName = function (name) {
this.name = name;
};
UserAdmin.prototype.getName = function () {
return this.name;
};
var user1 = new UserAdmin('liuzunkun');
var user2 = new UserAdmin('liuzunkun2');
// user1 === user2
console.log(user1, user2, user1 === user2);
console.log(user1.getName()); // liuzunkun
console.log(user2.getName()); // liuzunkun
user2.setName('liuzunkun3');
console.log(user1.getName()); // liuzunkun3
console.log(user2.getName()); // liuzunkun3
2. 静态方法 getInstance
此处的变化时在方法上假如 getInstance 保存 instance
js
function UserAdmin(name) {
this.name = name;
}
UserAdmin.getInstance = function (name) {
if (!UserAdmin.instance) {
UserAdmin.instance = new UserAdmin(name);
}
return UserAdmin.instance;
};
var user1 = UserAdmin.getInstance('liuzunkun');
var user2 = UserAdmin.getInstance('liuzunkun2');
3. class 类中实现
相应的在 class 类中是添加静态方法 getInstance
js
class UserAdmin {
constructor(name) {
if (UserAdmin.instance) return UserAdmin.instance;
this.name = name;
UserAdmin.instance = this;
}
getName(name) {
return this.name;
}
setName(name) {
this.name = name;
}
}
var user1 = new UserAdmin('liuzunkun');
var user2 = new UserAdmin('liuzunkun2');
4. 代理单例模式
js
function UserAdmin(name) {
this.name = name;
}
UserAdmin.prototype.setName = function (name) {
this.name = name;
};
UserAdmin.prototype.getName = function () {
return this.name;
};
var SingletonProxy = function (fn) {
let instance;
return function (...args) {
if (instance) return instance;
instance = new fn(...args);
return instance;
};
};
var UserAdminProxy = SingletonProxy(UserAdmin);
var user1 = new UserAdminProxy('liuzunkun');
var user2 = new UserAdminProxy('liuzunkun2');
惰性单例
假设我们有如下写法, 我们有针对 createLoginLayer
的单例写法,如果我们又有 createIframeLayer
呢,需要复制同样一份代码,这样程序不简洁,因此提取获取单例的公用方法。
js
var createLoginLayer = function () {
var div;
return function () {
if (!div) {
div = document.createElement('div');
div.innerHTML = 'LOGIN LAYER';
div.style.display = 'none';
document.body.appendChild(div);
}
return div;
};
};
document.getElementById('loginbtn').onclick = function () {
var loginLayer = createLoginLayer();
loginLayer.style.display = 'block';
};
下面的写法,将单例获取和核心操作分开处理。
js
var getSingle = function (fn) {
let result;
return function () {
return result || (result = fn.apply(this, arguments));
};
};
创建登录弹窗
js
var createLoginLayer = function () {
var div = document.createElement('div');
div.innerHTML = 'LOGIN LAYER';
div.style.display = 'none';
document.body.appendChild(div);
return div;
};
var createLoginLayerSingleton = getSingle(createLoginLayer);
document.getElementById('loginbtn').onclick = function () {
var loginLayer = createLoginLayerSingleton();
loginLayer.style.display = 'block';
};
此时 getSingle 可以被其他方法复用获取单例了。
参考
- JavaScript 设计模式与开发实践
- JavaScript 设计模式(一):单例模式|掘金