单例模式,是一种常用的软件设计模式;
单例模式就是确保一个类在任何情况下都只有一个实例,并提供一个全局访问点
类图
代码
ES6 单例模式
class Window {
constructor(name) {
this.name = name;
}
static getInstance(name) {
if (!this.instance) {
this.instance = new Window(name);
}
return this.instance;
}
}
var w1 = Window.getInstance();
var w2 = Window.getInstance();
console.log(w1 === w2); // true
ES5 单例模式
let Window = function (name) {
this.name = name;
}
Window.prototype.getName = function () {
console.log(this.name);
}
Window.getInstance = (function () {
let window = null; // 利用闭包,隐藏 window,不让外部获取到
return function (name) {
if (!window)
window = new Window(name);
return window;
}
})();
let w1 = Window.getInstance('zhangsan');
let w2 = Window.getInstance('zhangsan');
console.log(w1 === w2); // true
/*
* 问题:
* 1. 使用者必须知道这是一个单例的类,必须主动调用 getInstance
* 2. 并不能阻止使用者 new Window
*/
透明单例
// 解决了「ES5 单例模式」 的问题
let Window = (function () {
let windowInstance;
let Window = function (name) {
if (windowInstance) {
return windowInstance;
} else {
// 利用 new 的原理
this.name = name;
return (windowInstance = this);
}
}
Window.prototype.getName = function () {
console.log(this.name);
}
return Window;
})();
let w1 = new Window('zhangsan');
let w2 = new Window('zhangsan');
console.log(w1 === w2); // true
/*
* 问题:
* 1. 违反了 单一职责 原则
*/
单例与构建分离
// 解决了 「透明单例」 的问题
function Window(name) {
this.name = name;
}
Window.prototype.getName = function () {
console.log(this.name);
}
let createSingle = (function () {
let instance;
return function (name) {
if (!instance) {
instance = new Window();
}
return instance;
}
})();
let w1 = new createSingle('zhangsan');
let w2 = new createSingle('zhangsan');
console.log(w1 === w2); // true
/*
* 问题:
* 1. 只能创建单一实例,不灵活
*/
封装变化
function Dialog(name) {
this.name = name;
}
function Window(name) {
this.name = name;
}
Window.prototype.getName = function () {
console.log(this.name);
}
let createSingle = function (Constructor) {
let instance;
let SingleConstructor function () {
if (!instance) {
instance = new Constructor(...arguments);
}
return SingleConstructor;
}
};
let CreateWindow = createSingle(Window);
let w1 = new createSingle('zhangsan');
let w2 = new createSingle('zhangsan');
console.log(w1 === w2); // true
let CreateDialog = createSingle(Dialog);
let d1 = new createSingle('zhangsan');
let d2 = new createSingle('zhangsan');
console.log(d1 === d2); // true
命名空间
用户编写的代码与内部的类、函数、常量或第三方类、函数、常量之间的名字冲突;
为很长的标识符名称创建一个别名(或简短)的名称,提高源代码的可读性;
let utils = {};
utils.def = function (namespace, fn) {
let namespaces = namespace.split('.');
let fnName = namespaces.pop();
let current = utils;
for (let i = 0; i < namespaces.length; i++) {
let namespace = namespaces[i];
if (!current[namespace]) {
current[namespace] = {};
}
current = current[namespace];
}
current[fnName] = fn;
}
utils.def('dom.attr', function (key) {
console.log('dom.attr');
});
utils.def('dom.html', function (html) {
console.log('dom.html');
});
utils.def('string.trim', function () {
console.log('string.trim');
});
/*
生成的 utils 命名空间
utils = {
dom: {
attr: fn,
html: fn,
},
string: {
trim: fn,
},
}
*/
// 调用
utils.dom.attr('src');
utils.string.trim(' aaa ');
经典场景
jQuery
// 无论引入多少次 jQuery,都只会返回第一个 实例
if (window.jQuery != null) {
return window.jQuery;
} else {
//init~~~~~~~
}
模态窗口
class Login {
constructor() {
this.element = document.createElement('div');
this.element.innerHTML = (
`
用户名 <input type="text"/>
<button>登录</button>
`
);
this.element.style.cssText = 'width: 100px; height: 100px; position: absolute; left: 50%; top: 50%; display: block;';
document.body.appendChild(this.element);
}
show() {
this.element.style.display = 'block';
}
hide() {
this.element.style.display = 'none';
}
}
Login.getInstance = (function () {
let instance;
return function () {
if (!instance) {
instance = new Login();
}
return instance;
}
})();
document.getElementById('showBtn').addEventListener('click', function (event) {
Login.getInstance().show();
});
document.getElementById('hideBtn').addEventListener('click', function (event) {
Login.getInstance().hide();
});
LRU 缓存
-
为 LRU Cache 设计一个数据结构,它支持两个操作:
- get(key):如果 key 在 cache 中,则返回对应的 value 值,否则返回 -1;
- set(key, value):如果 key 不在 cache 中,则将该 (key, value) 插入 cache 中(注意,如果 cache 已满,则必须把最近最久未使用的元素从 cache 中删除);如果 key 在cache 中,则重置 value 的值;
-
实现代码:
// Vue3 的 keepalive 组件就用了这个 LRU 管理组件的缓存 class LRUCache { capacity: number map: Map<number, number> = new Map() constructor(capacity: number) { // 利用迭代器实现(map 是有序的) this.map = new Map() // 设置缓存最大个数 this.capacity = capacity } get(key: number): number { if (this.map.has(key)) { let value = this.map.get(key) // 重新 set,相当于更新到 map 最后 this.map.delete(key) this.map.set(key, value) return value } else { return -1 } } put(key: number, value: number): void { // 如果有,就删了再赋值(相当于更新到 map 最后) if (this.map.has(key)) { this.map.delete(key) } this.map.set(key, value) // 判断是不是容量超了,淘汰机制 if (this.map.size > this.capacity) { // this.map.keys() 返回一个 MapIterator {1, 3, 4} 迭代器对象 this.map.delete(this.map.keys().next().value) } } } LRUCache lRUCache = new LRUCache(2); lRUCache.put(1, 1); // 缓存是 {1=1} lRUCache.put(2, 2); // 缓存是 {1=1, 2=2} lRUCache.get(1); // 返回 1 lRUCache.put(3, 3); // 该操作会使得关键字 2 作废,缓存是 {1=1, 3=3} lRUCache.get(2); // 返回 -1 (未找到) lRUCache.put(4, 4); // 该操作会使得关键字 1 作废,缓存是 {4=4, 3=3} lRUCache.get(1); // 返回 -1 (未找到) lRUCache.get(3); // 返回 3 lRUCache.get(4); // 返回 4
工厂模式
上一篇