今天,与大家分享一下我对在项目中常见的设计模式的理解和认识。
什么是设计模式
设计模式就是针对项目中 特定的问题, 做出的简洁而且优化的处理方案
二、设计模式
单例模式
概念:同一个构造函数,生成唯一的实例,防止重复的实例化
优点:节省内存空间,提高程序执行效率
原理:判断构造函数是否生成过实例化对象,没有则new构造函数生成实例化对象,有则返回之前所new出的实例
核心代码:
//核心代码步骤
//1、创建一个构造函数,用来生成实例
function CreateObj(){}
//2、定义一个变量,可以不赋值,或赋值为空
let res = null;
//3、建立单例模式的函数
function Singleton(){
//如果res中存储的数值为原始数值,也就是我们定义的 null
//则说明没有new构造函数生成实例
//可以第一次执行构造函数
//将构造函数所实例化对象,存储在 res 中
//之后再次执行程序,res 中已经存储的是一个实例化对象
//res == null 则为false 便不再生成实例化对象
if(res == null){
//此时为第一次生成实例对象,存储在 res 中
res = new CreateObj();
}
//最终的返回值,就是我们创建的变量
//此变量则存储着 唯一 的实例化对象
//之后再次执行单例模式,便不再 重复 new 构造函数了
return res;
}
//4、通过单例模式,来生成实例化对象
const obj1 = Singleton();
const obj2 = Singleton();
//不通过单例模式的 (obj1 == obj2) 返回 false
//此时的 (obj1 == obj2) 则返回 true
console.log(obj1 == obj2);//true
注意: 在实际项目中,要用闭包的形式写,为了防止全局变量污染
组合模式
概念:通过一个‘遥控器’,控制所有‘启动’方法的执行
原理:将所需要执行‘启动’方法的实例对象,写入到一个数组中,通过循环遍历,来执行所有的启动方法
优点:
1、不必编写大量遍历数组或者其他数据结构的代码
2、形成一个层次体系,位于顶层的组合对象执行一个操作时,实际上是在对整个结构进行深度优先的搜索以及查找节点,这样查找、添加和删除都特别方便
缺点:由于组合对象调用的任何操作都会被传递到它的所有子对象,如果这个层次体系很大的话,系统性能将会受到影响
核心代码:
将所有执行方式一样的构造函数中所有的方法都放入这个 init 启动方法中并调用
// 组合模式小demo
// 目前没有大型项目做演示,看上去,组合模式貌似更麻烦
// 但组合模式在实际项目中是很有用,也很方便的
// 定义的几个执行方式一样的构造函数
示例
class A {
constructor() { }
//将此构造函数中所有的方法都放入这个 init 启动方法中
init() {
this.a1();
this.a2();
this.a3();
}
a1() {
console.log('a1');
}
a2() {
console.log('a2');
}
a3() {
console.log('a3');
}
}
class B {
constructor() { }
init() {
this.b1();
this.b2();
this.b3();
this.b4();
}
b1() {
console.log('b1');
}
b2() {
console.log('b2');
}
b3() {
console.log('b3');
}
b4() {
console.log('b4');
}
}
创建组合模式的构造函数
//通过组合模式来启动所有的启动方法
//组合模式的构造函数
class Compose {
constructor() {
//建立一个空数组,来储存需要启动的构造函数的实例对象
this.arr = [];
}
//将实例对象储存到数组中
add(obj) {
this.arr.push(obj);
}
//循环遍历这个数组中的实例化对象,调用其中的启动方法
excute() {
// 通过forEach循环遍历数组,其中item,存储的就是数组中的实例化对象
this.arr.forEach(function(item){
// 每个实例化对象都有一个启动方法 init()
item.init();
})
}
}
//下面的操作,就是通过组合模式来控制所有的启动方法
// on_off 就相当于总开关
const on_off = new Compose();
//执行 add()方法 就是向总开关中添加需要执行的构造函数的实例对象
on_off.add(new A());
on_off.add(new B());
on_off.add(new C());
//开启总开关,里边每一个构造函数都会被执行
on_off.excute();
观察者模式
概念:又叫 发布 - 订阅模式 / 消息模式
应用:一般与框架和双向数据绑定结合使用
核心代码概述:分为5个部分
1、主体对象
2、属性 – message盒子,存储执行类型和执行内容(以 对象/数组 形式存储)
3、add() 添加方法,向消息盒子中,添加需要执行的类型和内容
4、emit() 发布执行方法,发布消息盒子中类型里的内容
5、delete() 删除方法,删除消息盒子中与参数对应的类型内容
示例:
//观察者模式构造函数
class Observer {
constructor() {
//定义属性,来存储需要执行的函数
//这个属性也称为消息盒子
//一般是 对象/数组 形式
this.message = {};
}
//函数1:add()方法,向消息盒子中添加函数
//参数1:需要执行的事件类型
//参数2:需要执行的事件内容
add(type, fn) {
//判断这个类型是否已经存在
if (this.message[type]) {
//如果存在,则将此内容添加到这个类型里
this.message[type].push(fn);
} else {
//如果不存在,则创建此类型,并且将内容以数组的型式存储进去
this.message[type] = [fn];
}
}
//函数2:emit()方法,执行消息盒子中符合条件的函数
//参数1:需要执行的类型
//参数2:需要执行的内容
// 使用 ...形参 合并运算符 语法形式
// 将之后的实参,以数组的形式存储在形参中
emit(type, ...arr) {
//判断消息盒子中是否存在此类型,不存在则直接 return
if (!this.message[type]) {
return;
}
//这里可以通过双层 for 循环,循环遍历两个数组,数据相同则执行
//第一个数组 是消息盒子中 对应类型的数组 this.message[type]
//第二个数组 是形参传入的数组 ...arr
for (let i = 0; i < this.message[type].length; i++) {
for (let j = 0; j < arr.length; j++) {
//如果两个数组中的数据相同,则执行
if (arr[j] == this.message[type][i]) {
arr[j]();
}
}
}
}
//函数3:delete()方法,删除消息盒子中存储的函数
//参数1:需要删除的类型
//参数2:需要删除类型中的内容
delete(type, fn) {
//判断消息盒子中是否存在此类型,不存在则直接 return
if (!this.message[type]) {
return;
}
//如果有这个类型,则删除类型中对应的内容
//循环遍历数组,与需要删除的内容相同,则删除数组中的这个单元
//注意要防止数组坍塌,需要 i--
for (let i = 0; i < this.message[type].length; i++) {
if (fn == this.message[type][i]) {
//从当前索引开始,删除一个单元
this.message[type].splice(i, 1);
//防止数组坍塌,执行 i-- 操作
i--;
}
}
}
}
//生成实例化对象
const obj = new Observer();
//向观察者模式中添加,类型和操作内容(函数名)
obj.add("girlfriend", girl1);
obj.add("girlfriend", girl2);
obj.add("girlfriend", girl3);
obj.add("application", sleep);
obj.add("application", wash);
obj.add("application", money);
//执行发布操作
//第一个参数存储在 type 中,其他参数存储在 ...arr 中
obj.emit("girlfriend", girl1, girl2, girl3);
obj.emit("application", sleep, wash, money);
//调用删除方法
//删除类型中的内容
obj.delete("application", money);
//定义被发布的函数
function girl1() {
console.log("我是1号女友");
}
function girl2() {
console.log("我是2号女友");
}
function girl3() {
console.log("我是3号女友");
}
function sleep() {
console.log("给男朋友暖被窝");
}
function wash() {
console.log("给男朋友洗衣服");
}
function money() {
console.log("赚钱养男朋友");
}