一、单例模式
- 设计模式:
- 为了实现某一类功能给出的简洁而优化的解决方案
- 设计模式 - 单例模式
- 核心意义: 一个构造函数(类) 一生只有一个实例
- 思路:
- 实例化一个类的时候
- 需要先判断, 如果当前类以前被实例化过, 不需要再次实例化了
- 而是直接拿到原先的实例化对象
- 如果没有被实例化过, 那么新实例化一个实例对象
- 解决:
- 单独找一个变量, 初始化为 null
- 每一次创建实例的时候, 都判断一下该变量的值
- 如果为 null, 那么创建一个实例, 赋值给该变量
- 如果不为 null, 那么我们不再创建实例, 直接给出该变量的值
class Dialog {
constructor() {
console.log("在 页面中 创建了一个 弹出层");
}
}
let instance = null;
function newDialog() {
if (instance == null) {
instance = new Dialog();
}
return instance;
}
// 第一次调用, 创建一个 实例化对象然后给到 instance 中, 并返回 instance
let d1 = newDialog();
// 第二次调用, 直接返回 instance
let d2 = newDialog();
console.log(d1);
console.log(d2);
console.log(d1 === d2);
单例模式变形
-
单例模式问题
- 向外部写了一个全局变量 instance
- 多次调用时, 无法传递不一样的参数
- 构造函数的类名与创建实例的函数名是两个东西
-
解决:
- 利用闭包
let newDialog = (function fn() { let instance = null; return function () { if (instance == null) { instance = new Dialog(); } return instance; }; })();- 创建一个 init 方法
class Dialog { constructor() { console.log("在 页面中 创建了一个 弹出层, 文本为: "); this.title = ""; } setTitle(newTitle) { this.title = newTitle; console.log("将 title 属性 重新修改为了: ", this.title); } } let newDialog = (function outer() { let instance = null; return function inner(str) { // 没有接收到不同参数的原因在于, 这个 if 只有第一次才会执行, 后续永远不会执行 if (instance == null) { instance = new Dialog(); } // 但是, 这个位置却可以多次执行, 所以我们可以在这里, 给这个 类传递不同的参数 instance.setTitle(str); return instance; }; })();- 将类放在闭包函数内, 然后将函数名修改为类名
let Dialog = (function outer() { // 因为 Dialog 这个 类, 永远就使用一次, 所以函数局部 更合适 class Dialog { constructor() { console.log("在 页面中 创建了一个 弹出层, 文本为: "); this.title = ""; } setTitle(newTitle) { this.title = newTitle; console.log("将 title 属性 重新修改为了: ", this.title); } } let instance = null; return function inner(str) { if (instance == null) { instance = new Dialog(); } instance.setTitle(str); return instance; }; })();
二、策略模式
- 设计模式 - 策略模式
- 核心: 减少 if...else...
- 作用: 把多种形式的内容罗列出来
- 案例:
- 已知一个购物总价 1376
- 需要根据折扣计算最终价格
- 折扣种类: 8 折, 7 折, 300-30, 500-50
- 当前案例核心:
- 用一个数据结构, 把各种折扣记录下来
- 值存储的就是该折扣和总价的计算方式
const calcPrice = (function () {
const calcList = {
"80%": (total) => (total * 0.8).toFixed(2),
"70%": (total) => (total * 0.7).toFixed(2),
};
// 任何引用数据类型, 都可以当作 对象去使用
function inner(type, total) {
return calcList[type](total);
}
inner.add = function (type, fn) {
calcList[type] = fn;
};
inner.sub = function (type) {
delete calcList[type];
};
inner.getList = function () {
return calcList;
};
return inner;
})();
const res = calcPrice("80%", 1000);
console.log(res);
const res1 = calcPrice("70%", 1000);
console.log(res1);
calcPrice.add("300-30", (total) => total - Math.floor(total / 300) * 30);
const res2 = calcPrice("300-30", 1000);
console.log(res2);
calcPrice.sub("70%");
console.log(calcPrice.getList());
三、发布订阅模式
- 设计模式-发布订阅
- 首先明确 这个模式和一个叫做观察者模式类似但不是一个东西
- 目前一批开发人员认为这两个是一个东西, 另外一批认为是两个东西
- 案例: 买一本书
- 以前
- 去到书店, 问店员有没有 JS 从入门到入土
- 没有, 回去
- 一会再回去问, 有没有
- 没有, 回去
- 一会再回去问, 有没有
- ...
- 一会再回去问, 有没有
- 有了, 购买
- 现在
- 去到书店, 问店员有没有 JS 从入门到入土
- 没有, 给店员留下一个联系方式
- 直到店员给你打电话, 你触发技能(去买回来)
- 以前
- 在 JS 内 发布订阅 使用最多的地方 (addEventListener)
- xxx.addEventListener('click', fn)
- 思考: 以什么数据格式来记录
店员 ===
{
a: [fn1, fn2],
b: [fn3, fn4],
};
class Observer {
constructor(name) {
this.name = name; // 这步模拟店员名字, 可以不要
// 店员的记录手册
this.messages = {};
}
add(type, fn) {
if (this.messages[type] === undefined) this.messages[type] = [];
this.messages[type].push(fn);
}
remove(type, fn) {
if (this.messages[type] === undefined) return;
// if (fn === undefined) return this.messages[type] = []
if (fn === undefined) return delete this.messages[type];
this.messages[type] = this.messages[type].filter((item) => item !== fn);
}
trigger(type) {
// console.log(type)
// console.log(this.messages[type])
this.messages[type].forEach((item) => item());
}
}
const ob = new Observer("小李");
const A1 = () => {
console.log("我是 张三, 我要买这本书");
};
const A2 = () => {
console.log("我是 李四, 我要买这本书");
};
const B1 = () => {
console.log("我是 王五, 我要买这本书");
};
ob.add("A1", A1);
ob.add("A1", A2);
ob.add("B1", B1);
// ob.remove('A1', A1)
// ob.remove('A1')
ob.remove("C1");
ob.trigger("A1");
console.log(ob);