设计模式,面试必考的问题之一,许多新入行的同学可能感觉很高大上,然而其实日常开发中我们也或多或少的使用着设计模式,只是可能大家没有感知到,今天我们就来聊聊常见的一些设计模式和该模式在前端的使用场景!
迭代器模式
首先来看概念
迭代器模式定义: 提供一种方法顺序访问一个对象中的各元素,而又不需要暴露该对象的内部表示
迭代器模式可以简单理解为提供一个方法 完成forEach循环,对于js以及很多现代语言来说,都已经实现了内置迭代器模式
使用场景就很清晰:
只要需要遍历的场景都会使用到迭代器模式,可以把迭代的过程从业务逻辑中分离出来,而不用关心对象的内部结构
下面我们基于for循环简单实现一个迭代器模式
const myForEach = (arr, callback) => {
for (let i =0; i < arr.length; i++) {
callback.call(arr[i], i, arr[i])
}
}
// 测试
myForEach ([1,2,3], function(i,n){ console.log([i,n]) })
ps: 迭代器模式是一种相对简单的模式,绝大多数语言都已经内置了迭代器模块,我们只需要知道有这个模式就行
工厂模式
首先来看概念
工厂模式是最常用的一种用于创建对象的设计模式,其核心就是将逻辑封装在一个函数中不暴露创建对象的具体逻辑
简单来说,我们只要不让用户看到具体创建对象逻辑的方式,都可以称之为工厂模式,就类似工厂流水线,用户只能看到成品,看不到生产过程
基于这个实现思路也很多,我们简单使用构造函数实现下
class factory {
constructor (name, age) {
this.name = name
this.age = age
}
doSth () {
alert(`我的名字${this.anem},我的年龄${this.age}`)
}
}
const test1 = new factory ('张三', 18)
test1.doSth()
使用场景: 了解决多个类似对象声明
中介者模式
首先来看概念
中介者模式允许对象之间通过中介者对象进行通信,而不是直接相互引用
其实类似代理模式,都是通过第三方实现通讯,而不是直接访问对象,这种模式有助于减少对象之间的耦合性,使代码更加可维护和可扩展。
我们简单实现下中介者模式
/ 创建中介者对象
const Mediator = {
// 注册对象
participants: [],
// 添加参与者
addParticipant(participant) {
this.participants.push(participant);
},
// 发送消息
sendMessage(message, sender) {
for (let participant of this.participants) {
if (participant !== sender) {
participant.receiveMessage(message);
}
}
}
};
// 创建参与者对象
const Participant = {
// 初始化参与者
init(name) {
this.name = name;
Mediator.addParticipant(this);
},
// 接收消息
receiveMessage(message) {
console.log(`${this.name} 收到消息:${message}`);
},
// 发送消息
sendMessage(message) {
Mediator.sendMessage(message, this);
}
};
// 创建参与者
const participant1 = Object.create(Participant);
participant1.init('参与者1');
const participant2 = Object.create(Participant);
participant2.init('参与者2');
// 发送消息
participant1.sendMessage('你好!');
participant2.sendMessage('你好,参与者1!');
ps: 了解中介者模式即可,实际工作中使用较少
发布订阅模式 (观察者模式)
首先来看概念
发布订阅模式定义了对象间的一种一对多的关系,让多个观察者对象同时监听某一个主题对象,当一个对象发生改变时,所有依赖于它的对象都将得到通知
简单理解就是,所有用户订阅一个对象,这个对象发生变化时候,所有参与订阅的用户都会得到通知,就类似订阅公众号一样,有什么信息都会推送给订阅的用户
这是比较重要的一种设计模式, 我们熟悉的vue中的object.definePorperty就是典型使用了发布订阅模式
我们简单来看下object.definePorperty
const test = {
name: '张三',
age: 18
}
// 核心逻辑大致如下
object.keys(test).forEach((key) => {
Object.defineProperty(test, key, {
enumerable: true,
configurable: true,
get: () => {
console.log('获取到修改的数据');
},
set: (newName) => {
console.log('执行修改操作');
},
});
});
test.name = '李四'
console.log(test)
其中:
enumerable
:对象属性是否可通过for-in
循环,false
为不可循环,默认值为true
。configurable
:能否使用 ·、能否需改属性特性、或能否修改访问器属性,false
为不可重新定义,默认值为true
。
这块拓展下,vue3使用了proxy代理来取代object.definePorperty
至于为什么要选择proxy,已经成为老生常谈的面试题了,这块小伙伴们估计比我清楚,我就简单总结两点,其余的大家可以补充
首先,object.definePorperty不能监听到数组的变化,而且必须遍历对象的每个属性,比较耗费内存
proxy是js原生属性,直接代理整个对象,这样就减少了遍历属性的过程
最后,我们简单实现一个发布订阅模式
// 创建观察者对象
class Observer {
constructor() {
this.observers = [];
}
// 添加观察者
addObserver(observer) {
this.observers.push(observer);
}
// 移除观察者
removeObserver(observer) {
const index = this.observers.indexOf(observer);
if (index !== -1) {
this.observers.splice(index, 1);
}
}
// 通知观察者
notify(data) {
for (let observer of this.observers) {
observer.update(data);
}
}
}
// 创建具体观察者对象
class ConcreteObserver {
update(data) {
console.log(`收到更新:${data}`);
}
}
// 创建被观察者对象
class Subject {
constructor() {
this.observers = new Observer();
}
// 添加观察者
addObserver(observer) {
this.observers.addObserver(observer);
}
// 移除观察者
removeObserver(observer) {
this.observers.removeObserver(observer);
}
// 发送通知
notifyObservers(data) {
this.observers.notify(data);
}
}
// 创建观察者和被观察者对象
const observer1 = new ConcreteObserver();
const observer2 = new ConcreteObserver();
const subject = new Subject();
// 添加观察者到被观察者
subject.addObserver(observer1);
subject.addObserver(observer2);
// 发送通知
subject.notifyObservers('Hello World!');
// 移除观察者
subject.removeObserver(observer2);
// 再次发送通知
subject.notifyObservers('Goodbye!');
总结一下,发布订阅模式的优缺点
优点: 支持简单的广播通信。当对象状态发生改变时,会自动通知已经订阅过的对象
发布者与订阅者的耦合性降低
缺点: 耗时耗内存。创建订阅者需要消耗一定的时间和内存
维护相对困难
ps: 最近公司事比较多,更新不及时,实在抱歉