创建型模式
创建对象的模式, 抽象了实例化对象的过程
- 单例模式
定义:保证一个类仅有一个实例,并提供这个实例的全局访问的方法。
应用场景: 创建全局公用的实例对象,例如,vuex和redux中的store。
class Single {
constructor(name){
this.name = name;
}
static getInstance(name){
// 1. 创建全局的Symbol变量
const instance = Symbol.for('Single');
// 2. 如果instance不存在,创建新的Single实例,否则返回;
if(!Single[instance]){
Single[instance] = new Single(name)
}
return Single[instance]
}
}
测试代码:
const single-1 = Single.getInstance('Single-1'); // {name: 'Single-1'}
const single-2 = Single.getInstance('Single-2');// {name: 'Single-1'}
console.log(single-1 === single-2); // true
除此之外创建型模式中还有 工厂模式 超级工厂模式, 感兴趣的同学可以自行研究。
结构型模式
主要解决怎样组装现有的对象,设计交互方式,而达到实现一定的功能目的。
- 桥接模式 定义:将抽象部分和具体实现的部分分离,两者可独立变化,也可以一起工作。其实简单来书就是将函数传递给参数。
应用场景: 传递给组件的props,回调函数等等,例如数组map方法的回调函数。
// 自定义一个map函数
// map就相当于桥
const map = (arr,callback) => {
if(!Array.isArray(arr)&&!callback) return;
const returnArr = []
for (const [index,item] of arr.entries()) {
// 抽象部分
let value = callback(item,index)
if(value !== undefined){
returnArr.push(value)
}
}
return returnArr
}
测试代码:
const arr = [0,1,2,3,4,true,false];
map(arr,(item,index)=>{console.log(index,item); return item})
- 代理模式
定义:为一个对象提供一种代理以方便对这个对象的访问。
代理模式可以解决避免对一些对象的直接访问。
代理模式细分为以下几种:
虚拟代理(将开销大的运算延迟到需要时执行) 常见
缓存代理(为开销大的运算结果提供缓存)
保护代理(黑白双簧,代理充当黑脸,拦截非分要求) 常见
防火墙代理(控制网络资源的访问)
远程代理(为一个对象在不同的地址控件提供局部代表)
智能引用代理(访问对象执行一些附加操作)
写时复制代理(延迟对象复制过程,对象需要真正修改时才进行)
代理模式的优缺点: 代理模式有高度解耦,对象保护,方便修改的特点。 但是因为是通过代理访问对象,因此开销会更大,时间更慢。
保护代理:
//例子:代理接电话,实现黑名单
// 准备数据;
let backPhoneList = ['8888888888888'];
// 代理
const ProxyAcceptPhone = (phoneNumber) => {
// 预处理
console.log('电话正在接入!')
if(backPhoneList.includes(phoneNumber)){
//屏蔽
console.log("您的手机进入了黑名单");
} else {
//转接
AcceptPhone.call(this, phoneNumber);
}
}
// 本体
const AcceptPhone = (phoneNumber) => {
console.log(`电话已经打通,电话号码为${phoneNumber}`);
};
//外部调用代理
ProxyAcceptPhone("999999999999");
缓存代理:
// 例子 计算分数
// 本体
let countScore = (ansList) => {
let [a, b, c] = ansList;
return a + b + c;
};
// 代理
const proxyCountScore = (() => {
let existScore = {}; //存储对象
return (ansList) => {
let attr = ansList.join(",");
if (existScore[attr] != null) {
return existScore[attr];
} else {
return (existScore[attr] = countScore(ansList));
}
};
})();
// 调用代理
proxyCountScore([0, 1, 1]);
虚拟代理:
对虚拟代理运用最常见的就是图片预加载,常见的做法事先用一张loading图片占位,然后异步加载图片,待图片加载完成,把其填充到img节点里。
//虚拟代理的目的,是将开销大的运算延迟到需要时再执行。
// 本体
const setImage = (function(){
const imageNode = document.createElement("img");
document.body.appendChild(imageNode);
return {
setSrc(src){
imageNode.src = src;
}
}
})();
// 代理
const proxyImage = (function(){
const img = new Image();
img.onload = function(){
setImage.setSrc(this.src); // 图片加载完成设置真实的图片src
};
return {
setSrc(src){
setImage.setSrc('loading图片路径'); // 设置图片的src为loading图;
img.src = src;
}
}
})()
// 外部调用
proxyImage.setSrc("真实的图片地址") // 有loading图片的预加载效果
- 组合模式
定义:将对象组合成树形结构,以表示 部分-整体 的层次结构。
应用场景: 组合模式可以再需要针对'树形结构'进行操作的应用中使用,比如扫描文件夹,渲染网站导航结构等等。
// 组合模式
// 树的根节点,树对象
// 类目 类
class Category {
constructor(name){
this.name = name;
this.subClass = [];
}
add(subClass){
this.subClass.push(subClass);
}
scan(){
// 遍历调用叶对象
for(const subClass of this.subClass ){
subClass.scan();
}
}
}
// 子类 类
class SubClass {
constructor(name){
this.name = name;
}
add() {
throw new Error("子类不能再添加子类了");
}
scan() {
console.log(`${this.name}`)
}
}
测试代码:
// 创建类目对象
const movie = new Category("电影");
const music = new Category("音乐");
const media = new Category("娱乐");
// 创建子类目对象
const movieSubClass = new SubClass('复仇者联盟');
const musicSubClass = new SubClass('我为祖国献石油'); // 打败夏洛的双截棍
// 组合
movie.add(movieSubClass)
music.add(musicSubClass)
media.add(movie)
media.add(music)
行为型模式
定义:描述多个类或对象怎样交互以及怎样分配职责。
- 发布-订阅模式
定义:定义了对象之间的一种一对多的依赖关系,当一个对象的状态改变时,所有依赖它的对象都可以得到通知。
class Event {
constructor(){
this.eventListObj = {}; // 缓存事件列表对象
}
// 单例模式
static getInstance() {
const instance = Symbol.for("instance");
if (!Event[instance]) {
Event[instance] = new Event();
}
return Event[instance];
}
// 添加监听事件,同一个指令,可以有多个事件
listen(key, fn){
if(!this.eventListObj[key]){
this.eventListObj[key] = []
}
// 订阅消息添加进缓存列表
this.eventListObj[key].push(fn);
}
// 触发监听事件
trigger(key, ...args){
const fns = this.eventListObj[key]
if(fns && fns.length) {
for(const item of fns){
item.apply(this, args)
}
}
}
// 移除事件
remove(key, fn){
const fns = this.eventListObj[key];
// 被订阅过才会操作
if(fns){
if(!fn){
fns.length = 0;
} else {
for(const [index,item] of fns.entries()){
if(item === fn){
fns.splice(index, 1)
}
}
}
}
}
}
测试代码:
//注册全局的发布者
const event = Event.getInstance();
//创建回调函数
const add = (a, b) => {
console.log(a + b);
};
const minus = (a, b) => {
console.log(a - b);
};
//订阅加法消息
event.listen("add", add);
//订阅减法消息
event.listen("minus", minus);
//触发加法订阅消息
event.trigger("add", 1, 2);
//触发减法订阅消息
event.trigger("minus", 3, 1);
event.remove("add", add);
- 观察者模式 定义: 当一个对象被修改时,则会自动通知依赖它的对象。概念几乎和发布-订阅的概念相同。
class Subject {
constructor() {
this.observers = []; // 观察者列表
}
add(observer) {
this.observers.push(observer); // 添加观察者
}
remove(observer) {
let index = this.observers.findIndex((item) => item === observer); // 找到要删除的观察者
index > -1 && this.observers.splice(index, 1);
}
notify() {
// 通知
for (const observer of this.observers) {
observer.update();
}
}
}
// 观察者
class Observer {
constructor(name) {
this.name = name;
}
// 目标对象跟新是触发的回调
update() {
console.log(`目标者通知我更新了:${this.name}`);
}
}
测试代码:
const sub = new Subject()
const ob = new Observer('Observer')
sub.add(ob)
sub.notify(); // 目标者通知我更新了:Observer